From 5337a4e78f62a9a484d2332cf95474fcc91b13b0 Mon Sep 17 00:00:00 2001
From: Derek Lesho <dlesho@codeweavers.com>
Date: Tue, 5 May 2020 09:45:24 -0500
Subject: [PATCH 01/46] mf: Add missing ::Release of clock timer.

Signed-off-by: Derek Lesho <dlesho@codeweavers.com>
---
 dlls/mf/session.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/dlls/mf/session.c b/dlls/mf/session.c
index 75ef600a4d..d6b881218b 100644
--- a/dlls/mf/session.c
+++ b/dlls/mf/session.c
@@ -4289,6 +4289,7 @@ static HRESULT WINAPI present_clock_timer_callback_Invoke(IMFAsyncCallback *ifac
 
     EnterCriticalSection(&clock->cs);
     list_remove(&timer->entry);
+    IUnknown_Release(&timer->IUnknown_iface);
     LeaveCriticalSection(&clock->cs);
 
     IMFAsyncCallback_Invoke(timer->callback, timer->result);
-- 
2.25.3

From 363c90e81e451c946f52fca4fd11bb777f3a4565 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Sergio=20G=C3=B3mez=20Del=20Real?=
 <sdelreal@codeweavers.com>
Date: Wed, 1 Apr 2020 16:11:05 -0500
Subject: [PATCH 02/46] mf/tests: Sink objects are stream sinks, not media
 sinks.
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Signed-off-by: Sergio Gómez Del Real <sdelreal@codeweavers.com>
---
 dlls/mf/tests/mf.c | 7 +++++--
 1 file changed, 5 insertions(+), 2 deletions(-)

diff --git a/dlls/mf/tests/mf.c b/dlls/mf/tests/mf.c
index bf14ccf0fe..3a0164c851 100644
--- a/dlls/mf/tests/mf.c
+++ b/dlls/mf/tests/mf.c
@@ -1311,6 +1311,7 @@ static void test_topology_loader(void)
     IMFPresentationDescriptor *pd;
     IMFSourceResolver *resolver;
     IMFActivate *sink_activate;
+    IMFStreamSink *stream_sink;
     unsigned int count, value;
     IMFMediaType *media_type;
     IMFStreamDescriptor *sd;
@@ -1423,7 +1424,10 @@ todo_wine
     hr = IMFActivate_ActivateObject(sink_activate, &IID_IMFMediaSink, (void **)&sink);
     ok(hr == S_OK, "Failed to activate, hr %#x.\n", hr);
 
-    hr = IMFTopologyNode_SetObject(sink_node, (IUnknown *)sink);
+    hr = IMFMediaSink_GetStreamSinkByIndex(sink, 0, &stream_sink);
+    ok(hr == S_OK, "Failed to get stream sink, hr %#x.\n", hr);
+
+    hr = IMFTopologyNode_SetObject(sink_node, (IUnknown *)stream_sink);
     ok(hr == S_OK, "Failed to set object, hr %#x.\n", hr);
 
     hr = IMFTopology_GetCount(topology, &count);
@@ -1431,7 +1435,6 @@ todo_wine
     ok(count == 0, "Unexpected count %u.\n", count);
 
     hr = IMFTopoLoader_Load(loader, topology, &full_topology, NULL);
-todo_wine
     ok(hr == S_OK, "Failed to resolve topology, hr %#x.\n", hr);
     ok(full_topology != topology, "Unexpected instance.\n");
 
-- 
2.25.3

From 31ca43bd32fdf3f9712fdcfc2a12e9495d4e6eb1 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Sergio=20G=C3=B3mez=20Del=20Real?=
 <sdelreal@codeweavers.com>
Date: Wed, 1 Apr 2020 16:11:06 -0500
Subject: [PATCH 03/46] mf/tests: Add tests for the topology loader.
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Signed-off-by: Sergio Gómez Del Real <sdelreal@codeweavers.com>
---
 dlls/mf/tests/mf.c | 435 ++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 433 insertions(+), 2 deletions(-)

diff --git a/dlls/mf/tests/mf.c b/dlls/mf/tests/mf.c
index 3a0164c851..3ce94862c7 100644
--- a/dlls/mf/tests/mf.c
+++ b/dlls/mf/tests/mf.c
@@ -1303,26 +1303,169 @@ static const IMFSampleGrabberSinkCallbackVtbl test_grabber_callback_vtbl =
     test_grabber_callback_OnShutdown,
 };
 
+static HRESULT WINAPI test_media_source_QueryInterface(IMFMediaSource *iface, REFIID riid, void **out)
+{
+    if (IsEqualIID(riid, &IID_IMFMediaSource) ||
+        IsEqualIID(riid, &IID_IMFMediaEventGenerator) ||
+        IsEqualIID(riid, &IID_IUnknown))
+    {
+        *out = iface;
+        IUnknown_AddRef((IUnknown*)*out);
+        return S_OK;
+    }
+
+    *out = NULL;
+    return E_NOINTERFACE;
+}
+
+static ULONG WINAPI test_media_source_AddRef(IMFMediaSource *iface)
+{
+    return 2;
+}
+
+static ULONG WINAPI test_media_source_Release(IMFMediaSource *iface)
+{
+    return 1;
+}
+
+static HRESULT WINAPI test_media_source_GetEvent(IMFMediaSource *iface, DWORD flags, IMFMediaEvent **event)
+{
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI test_media_source_BeginGetEvent(IMFMediaSource *iface, IMFAsyncCallback *callback, IUnknown *state)
+{
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI test_media_source_EndGetEvent(IMFMediaSource *iface, IMFAsyncResult *result, IMFMediaEvent **event)
+{
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI test_media_source_QueueEvent(IMFMediaSource *iface, MediaEventType event_type, REFGUID ext_type,
+        HRESULT hr, const PROPVARIANT *value)
+{
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI test_media_source_GetCharacteristics(IMFMediaSource *iface, DWORD *characteristics)
+{
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI test_media_source_CreatePresentationDescriptor(IMFMediaSource *iface, IMFPresentationDescriptor **descriptor)
+{
+    HRESULT hr;
+
+    IMFPresentationDescriptor *pd;
+    IMFMediaType *mediatypes[3];
+    IMFStreamDescriptor *sd[2];
+
+    if (FAILED(hr = MFCreateMediaType(&mediatypes[0])))
+        return hr;
+    if (FAILED(hr = IMFMediaType_SetGUID(mediatypes[0], &MF_MT_MAJOR_TYPE, &MFMediaType_Video)))
+        return hr;
+    if (FAILED(hr = IMFMediaType_SetGUID(mediatypes[0], &MF_MT_SUBTYPE, &MFVideoFormat_YUY2)))
+        return hr;
+    if (FAILED(hr = MFCreateMediaType(&mediatypes[1])))
+        return hr;
+    if (FAILED(hr = IMFMediaType_SetGUID(mediatypes[1], &MF_MT_MAJOR_TYPE, &MFMediaType_Audio)))
+        return hr;
+    if (FAILED(hr = IMFMediaType_SetGUID(mediatypes[1], &MF_MT_SUBTYPE, &MFAudioFormat_MP3)))
+        return hr;
+    if (FAILED(hr = IMFMediaType_SetUINT32(mediatypes[1], &MF_MT_AUDIO_SAMPLES_PER_SECOND, 32000)))
+        return hr;
+    if (FAILED(hr = IMFMediaType_SetUINT32(mediatypes[1], &MF_MT_AUDIO_NUM_CHANNELS, 2)))
+        return hr;
+    if (FAILED(hr = IMFMediaType_SetUINT32(mediatypes[1], &MF_MT_AUDIO_PREFER_WAVEFORMATEX, 1)))
+        return hr;
+    if (FAILED(hr = MFCreateMediaType(&mediatypes[2])))
+        return hr;
+    if (FAILED(hr = IMFMediaType_SetGUID(mediatypes[2], &MF_MT_MAJOR_TYPE, &MFMediaType_Audio)))
+        return hr;
+    if (FAILED(hr = IMFMediaType_SetGUID(mediatypes[2], &MF_MT_SUBTYPE, &MFAudioFormat_PCM)))
+        return hr;
+    if (FAILED(hr = MFCreateStreamDescriptor(0, 3, mediatypes, &sd[0])))
+        return hr;
+    if (FAILED(hr = MFCreateStreamDescriptor(1, 3, mediatypes, &sd[1])))
+        return hr;
+    if (FAILED(hr = MFCreatePresentationDescriptor(2, sd, &pd)))
+        return hr;
+    *descriptor = pd;
+
+    IMFMediaType_Release(mediatypes[0]);
+    IMFMediaType_Release(mediatypes[1]);
+    IMFMediaType_Release(mediatypes[2]);
+    IMFStreamDescriptor_Release(sd[0]);
+    IMFStreamDescriptor_Release(sd[1]);
+
+    return S_OK;
+}
+
+static HRESULT WINAPI test_media_source_Start(IMFMediaSource *iface, IMFPresentationDescriptor *descriptor,
+                                     const GUID *time_format, const PROPVARIANT *start_position)
+{
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI test_media_source_Stop(IMFMediaSource *iface)
+{
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI test_media_source_Pause(IMFMediaSource *iface)
+{
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI test_media_source_Shutdown(IMFMediaSource *iface)
+{
+    return S_OK;
+}
+
+static const IMFMediaSourceVtbl test_media_source_vtbl =
+{
+    test_media_source_QueryInterface,
+    test_media_source_AddRef,
+    test_media_source_Release,
+    test_media_source_GetEvent,
+    test_media_source_BeginGetEvent,
+    test_media_source_EndGetEvent,
+    test_media_source_QueueEvent,
+    test_media_source_GetCharacteristics,
+    test_media_source_CreatePresentationDescriptor,
+    test_media_source_Start,
+    test_media_source_Stop,
+    test_media_source_Pause,
+    test_media_source_Shutdown,
+};
+
 static void test_topology_loader(void)
 {
     IMFSampleGrabberSinkCallback test_grabber_callback = { &test_grabber_callback_vtbl };
+    IMFMediaSource test_media_source = { &test_media_source_vtbl };
     IMFTopology *topology, *topology2, *full_topology;
     IMFTopologyNode *src_node, *sink_node;
+    IMFMediaType *media_type, *current_media_type;
     IMFPresentationDescriptor *pd;
     IMFSourceResolver *resolver;
     IMFActivate *sink_activate;
     IMFStreamSink *stream_sink;
     unsigned int count, value;
-    IMFMediaType *media_type;
+    IMFMediaTypeHandler *mth;
     IMFStreamDescriptor *sd;
+    UINT32 enum_src, method;
     MF_OBJECT_TYPE obj_type;
     IMFMediaSource *source;
     IMFTopoLoader *loader;
     IMFByteStream *stream;
     IMFAttributes *attr;
     IMFMediaSink *sink;
+    WORD node_count;
     WCHAR *filename;
     BOOL selected;
+    DWORD flags;
     HRESULT hr;
     GUID guid;
 
@@ -1438,6 +1581,9 @@ todo_wine
     ok(hr == S_OK, "Failed to resolve topology, hr %#x.\n", hr);
     ok(full_topology != topology, "Unexpected instance.\n");
 
+    IMFTopology_GetNodeCount(full_topology, &node_count);
+    ok(node_count == 2, "Topology node count is %#x.\n", node_count);
+
     hr = IMFTopology_GetCount(topology, &count);
     ok(hr == S_OK, "Failed to get attribute count, hr %#x.\n", hr);
     ok(count == 0, "Unexpected count %u.\n", count);
@@ -1464,10 +1610,295 @@ todo_wine {
 
     IMFTopology_Release(topology2);
     IMFTopology_Release(full_topology);
+    IMFByteStream_Release(stream);
+    IMFStreamDescriptor_Release(sd);
 
+    /* test with stream deselected */
+    IMFPresentationDescriptor_GetStreamDescriptorCount(pd, &count);
+    ok(count == 1, "Unexpected stream descriptor count.\n");
+    hr = IMFPresentationDescriptor_DeselectStream(pd, 0);
+    ok(hr == S_OK, "Failed to deselect stream, hr %#x.\n", hr);
+    hr = IMFTopoLoader_Load(loader, topology, &full_topology, NULL);
+    ok(hr == S_OK, "Failed to resolve topology, hr %#x.\n", hr);
+    IMFTopologyNode_Release(src_node);
+    hr = IMFTopology_GetNode(full_topology, 0, &src_node);
+    ok(hr == S_OK, "Failed to get full topology source node, hr %#x.\n", hr);
+    IMFPresentationDescriptor_Release(pd);
+    hr = IMFTopologyNode_GetUnknown(src_node, &MF_TOPONODE_PRESENTATION_DESCRIPTOR, &IID_IMFPresentationDescriptor, (void **)&pd);
+    ok(hr == S_OK, "Failed to get presentation descriptor, hr %#x.\n", hr);
+    IMFStreamDescriptor_Release(sd);
+    IMFPresentationDescriptor_GetStreamDescriptorByIndex(pd, 0, &selected, &sd);
+    ok(hr == S_OK, "Failed to get stream descriptor, hr %#x.\n", hr);
+    ok(!selected, "Stream should not be selected\n.");
+
+    IMFPresentationDescriptor_Release(pd);
+    IMFTopologyNode_Release(src_node);
+    IMFTopology_Release(full_topology);
+    IMFTopology_Release(topology);
     IMFMediaSource_Release(source);
     IMFSourceResolver_Release(resolver);
-    IMFByteStream_Release(stream);
+
+    /* test source stream with various media types */
+    /* first, test default behavior; MF_TOPOLOGY_ENUMERATE_SOURCE_TYPES not set */
+    hr = MFCreateTopology(&topology);
+    ok(hr == S_OK, "Failed to create topology, hr %#x.\n", hr);
+
+    hr = IMFMediaSource_CreatePresentationDescriptor(&test_media_source, &pd);
+    ok(hr == S_OK, "Failed to create descriptor, hr %#x.\n", hr);
+
+    hr = IMFPresentationDescriptor_GetStreamDescriptorByIndex(pd, 0, &selected, &sd);
+    ok(hr == S_OK, "Failed to get stream descriptor, hr %#x.\n", hr);
+
+    hr = IMFStreamDescriptor_GetMediaTypeHandler(sd, &mth);
+    ok(hr == S_OK, "Failed to get media type handler, hr %#x.\n", hr);
+    hr = IMFMediaTypeHandler_GetCurrentMediaType(mth, &media_type);
+    ok(hr == MF_E_NOT_INITIALIZED, "Unexpected hr %#x.\n", hr);
+
+    hr = MFCreateTopologyNode(MF_TOPOLOGY_SOURCESTREAM_NODE, &src_node);
+    ok(hr == S_OK, "Failed to create topology node, hr %#x.\n", hr);
+
+    hr = IMFTopologyNode_SetUnknown(src_node, &MF_TOPONODE_SOURCE, (IUnknown *)&test_media_source);
+    ok(hr == S_OK, "Failed to set node source, hr %#x.\n", hr);
+    hr = IMFTopologyNode_SetUnknown(src_node, &MF_TOPONODE_PRESENTATION_DESCRIPTOR, (IUnknown *)pd);
+    ok(hr == S_OK, "Failed to set node pd, hr %#x.\n", hr);
+    hr = IMFTopologyNode_SetUnknown(src_node, &MF_TOPONODE_STREAM_DESCRIPTOR, (IUnknown *)sd);
+    ok(hr == S_OK, "Failed to set node sd, hr %#x.\n", hr);
+
+    hr = IMFTopology_AddNode(topology, src_node);
+    ok(hr == S_OK, "Failed to add a node, hr %#x.\n", hr);
+
+    hr = MFCreateMediaType(&media_type);
+    ok(hr == S_OK, "Failed to create media type, hr %#x.\n", hr);
+
+    hr = IMFMediaType_SetGUID(media_type, &MF_MT_MAJOR_TYPE, &MFMediaType_Audio);
+    ok(hr == S_OK, "Failed to set attribute, hr %#x.\n", hr);
+    hr = IMFMediaType_SetGUID(media_type, &MF_MT_SUBTYPE, &MFAudioFormat_PCM);
+    ok(hr == S_OK, "Failed to set attribute, hr %#x.\n", hr);
+
+    hr = MFCreateSampleGrabberSinkActivate(media_type, &test_grabber_callback, &sink_activate);
+    ok(hr == S_OK, "Failed to create grabber sink, hr %#x.\n", hr);
+    IMFMediaType_Release(media_type);
+
+    hr = IMFActivate_ActivateObject(sink_activate, &IID_IMFMediaSink, (void **)&sink);
+    ok(hr == S_OK, "Failed to activate, hr %#x.\n", hr);
+
+    hr = IMFMediaSink_GetStreamSinkByIndex(sink, 0, &stream_sink);
+    ok(hr == S_OK, "Failed to get stream sink, hr %#x.\n", hr);
+
+    hr = MFCreateTopologyNode(MF_TOPOLOGY_OUTPUT_NODE, &sink_node);
+    ok(hr == S_OK, "Failed to create output node, hr %#x.\n", hr);
+
+    hr = IMFTopologyNode_SetObject(sink_node, (IUnknown *)stream_sink);
+    ok(hr == S_OK, "Failed to set object, hr %#x.\n", hr);
+
+    hr = IMFTopology_AddNode(topology, sink_node);
+    ok(hr == S_OK, "Failed to add sink node, hr %#x.\n", hr);
+
+    hr = IMFTopologyNode_ConnectOutput(src_node, 0, sink_node, 0);
+    ok(hr == S_OK, "Failed to connect nodes, hr %#x.\n", hr);
+
+    hr = IMFTopology_GetUINT32(topology, &MF_TOPOLOGY_ENUMERATE_SOURCE_TYPES, &enum_src);
+    ok(hr == MF_E_ATTRIBUTENOTFOUND, "Attribute should not be set\n.");
+    /* if no current media type set, loader uses first index exclusively */
+    hr = IMFTopoLoader_Load(loader, topology, &full_topology, NULL);
+todo_wine
+    /* when major types differ, error is MF_E_TOPO_CODEC_NOT_FOUND */
+    ok(hr == MF_E_TOPO_CODEC_NOT_FOUND, "Unexpected hr %#x.\n", hr);
+
+    hr = IMFMediaTypeHandler_GetMediaTypeByIndex(mth, 2, &media_type);
+    ok(hr == S_OK, "Failed getting media type, hr %#x.\n", hr);
+
+    /* setting current media type overrides previous behavior; tries with it, and only with it */
+    hr = IMFMediaTypeHandler_SetCurrentMediaType(mth, media_type);
+    ok(hr == S_OK, "Failed setting current media type, hr %#x.\n", hr);
+
+    hr = IMFTopoLoader_Load(loader, topology, &full_topology, NULL);
+    ok(hr == S_OK, "Unexpected hr %#x.\n", hr);
+
+    hr = IMFTopology_GetNodeCount(full_topology, &node_count);
+    ok(hr == S_OK, "Failed to get node count, hr %#x.\n", hr);
+    ok(node_count == 2, "Unexpected node count.\n");
+
+    hr = IMFMediaTypeHandler_GetMediaTypeByIndex(mth, 1, &media_type);
+    ok(hr == S_OK, "Failed getting media type, hr %#x.\n", hr);
+
+    hr = IMFMediaTypeHandler_SetCurrentMediaType(mth, media_type);
+    ok(hr == S_OK, "Failed setting current media type, hr %#x.\n", hr);
+
+    hr = IMFTopoLoader_Load(loader, topology, &full_topology, NULL);
+    ok(hr == S_OK, "Failed to resolve topology, hr %#x.\n", hr);
+
+    hr = IMFTopology_GetNodeCount(full_topology, &node_count);
+    ok(hr == S_OK, "Failed to get node count, hr %#x.\n", hr);
+todo_wine
+    ok(node_count == 3, "Unexpected node count.\n");
+
+    IMFTopology_Release(full_topology);
+
+    /* now test with MF_TOPOLOGY_ENUMERATE_SOURCE_TYPES set on topology */
+    hr = IMFTopology_SetUINT32(topology, &MF_TOPOLOGY_ENUMERATE_SOURCE_TYPES, 1);
+    ok(hr == S_OK, "Failed setting attribute\n.");
+    hr = IMFMediaTypeHandler_GetMediaTypeByIndex(mth, 2, &media_type);
+    ok(hr == S_OK, "Failed getting media type, hr %#x.\n", hr);
+
+    /* first, if MF_CONNECT_RESOLVE_INDEPENDENT_OUTPUTTYPES is not set on source */
+    IMFTopologyNode_GetUINT32(src_node, &MF_TOPONODE_CONNECT_METHOD, &method);
+    ok(hr == S_OK, "Attribute should be set\n.");
+    IMFTopologyNode_SetUINT32(src_node, &MF_TOPONODE_CONNECT_METHOD, method & ~MF_CONNECT_RESOLVE_INDEPENDENT_OUTPUTTYPES);
+    ok(hr == S_OK, "Failed setting attribute %#x\n.", hr);
+
+    hr = IMFTopoLoader_Load(loader, topology, &full_topology, NULL);
+    ok(hr == S_OK, "Failed to resolve topology, hr %#x.\n", hr);
+
+    hr = IMFTopology_GetNodeCount(full_topology, &node_count);
+    ok(hr == S_OK, "Failed to get node count, hr %#x.\n", hr);
+    ok(node_count == 2, "Unexpected node count.\n");
+    IMFTopologyNode_Release(src_node);
+    hr = IMFTopology_GetNode(full_topology, 0, &src_node);
+    ok(hr == S_OK, "Failed to get node, hr %#x.\n", hr);
+    IMFStreamDescriptor_Release(sd);
+    hr = IMFTopologyNode_GetUnknown(src_node, &MF_TOPONODE_STREAM_DESCRIPTOR, &IID_IMFStreamDescriptor, (void **)&sd);
+    ok(hr == S_OK, "Failed to get stream descriptor, hr %#x.\n", hr);
+    IMFMediaTypeHandler_Release(mth);
+    hr = IMFStreamDescriptor_GetMediaTypeHandler(sd, &mth);
+    ok(hr == S_OK, "Failed to get media type handler, hr %#x.\n", hr);
+    hr = IMFMediaTypeHandler_GetCurrentMediaType(mth, &current_media_type);
+    ok(hr == S_OK, "Failed to get current media type, hr %#x.\n", hr);
+    hr = IMFMediaType_IsEqual(current_media_type, media_type, &flags);
+todo_wine {
+    ok(hr == S_OK, "Unexpected hr %#x.\n", hr);
+    ok(flags & MF_MEDIATYPE_EQUAL_FORMAT_DATA, "Types should be equal.\n");
+}
+
+    IMFMediaType_Release(media_type);
+    IMFMediaType_Release(current_media_type);
+    IMFMediaTypeHandler_Release(mth);
+    IMFStreamSink_Release(stream_sink);
+    IMFMediaSink_Release(sink);
+    IMFActivate_Release(sink_activate);
+    IMFStreamDescriptor_Release(sd);
+
+    /* add second branch with a valid first branch */
+    hr = IMFTopology_SetUINT32(topology, &MF_TOPOLOGY_ENUMERATE_SOURCE_TYPES, 0);
+    ok(hr == S_OK, "Failed setting attribute\n.");
+    IMFTopologyNode_Release(src_node);
+    hr = IMFTopology_GetNode(topology, 0, &src_node);
+    ok(hr == S_OK, "Failed to get node, hr %#x.\n", hr);
+    hr = IMFTopologyNode_GetUnknown(src_node, &MF_TOPONODE_STREAM_DESCRIPTOR, &IID_IMFStreamDescriptor, (void **)&sd);
+    ok(hr == S_OK, "Failed to get stream descriptor, hr %#x.\n", hr);
+    hr = IMFStreamDescriptor_GetMediaTypeHandler(sd, &mth);
+    ok(hr == S_OK, "Failed to get media type handler, hr %#x.\n", hr);
+    hr = IMFMediaTypeHandler_GetMediaTypeByIndex(mth, 1, &media_type);
+    ok(hr == S_OK, "Failed getting media type, hr %#x.\n", hr);
+    hr = IMFMediaTypeHandler_SetCurrentMediaType(mth, media_type);
+    ok(hr == S_OK, "Failed setting current media type, hr %#x.\n", hr);
+
+    hr = IMFTopoLoader_Load(loader, topology, &full_topology, NULL);
+    ok(hr == S_OK, "Failed to resolve topology, hr %#x.\n", hr);
+
+    IMFTopology_Release(full_topology);
+
+    IMFStreamDescriptor_Release(sd);
+    hr = IMFPresentationDescriptor_GetStreamDescriptorByIndex(pd, 1, &selected, &sd);
+    ok(hr == S_OK, "Failed to get stream descriptor, hr %#x.\n", hr);
+
+    hr = IMFStreamDescriptor_GetMediaTypeHandler(sd, &mth);
+    ok(hr == S_OK, "Failed to get media type handler, hr %#x.\n", hr);
+    hr = IMFMediaTypeHandler_GetCurrentMediaType(mth, &media_type);
+    ok(hr == MF_E_NOT_INITIALIZED, "Unexpected hr %#x.\n", hr);
+
+    IMFTopologyNode_Release(src_node);
+    hr = MFCreateTopologyNode(MF_TOPOLOGY_SOURCESTREAM_NODE, &src_node);
+    ok(hr == S_OK, "Failed to create topology node, hr %#x.\n", hr);
+
+    hr = IMFTopologyNode_SetUnknown(src_node, &MF_TOPONODE_SOURCE, (IUnknown *)&test_media_source);
+    ok(hr == S_OK, "Failed to set node source, hr %#x.\n", hr);
+    hr = IMFTopologyNode_SetUnknown(src_node, &MF_TOPONODE_PRESENTATION_DESCRIPTOR, (IUnknown *)pd);
+    ok(hr == S_OK, "Failed to set node pd, hr %#x.\n", hr);
+    hr = IMFTopologyNode_SetUnknown(src_node, &MF_TOPONODE_STREAM_DESCRIPTOR, (IUnknown *)sd);
+    ok(hr == S_OK, "Failed to set node sd, hr %#x.\n", hr);
+
+    hr = IMFTopology_AddNode(topology, src_node);
+    ok(hr == S_OK, "Failed to add a node, hr %#x.\n", hr);
+
+    hr = MFCreateMediaType(&media_type);
+    ok(hr == S_OK, "Failed to create media type, hr %#x.\n", hr);
+
+    hr = IMFMediaType_SetGUID(media_type, &MF_MT_MAJOR_TYPE, &MFMediaType_Audio);
+    ok(hr == S_OK, "Failed to set attribute, hr %#x.\n", hr);
+    hr = IMFMediaType_SetGUID(media_type, &MF_MT_SUBTYPE, &MFAudioFormat_PCM);
+    ok(hr == S_OK, "Failed to set attribute, hr %#x.\n", hr);
+
+    hr = MFCreateSampleGrabberSinkActivate(media_type, &test_grabber_callback, &sink_activate);
+    ok(hr == S_OK, "Failed to create grabber sink, hr %#x.\n", hr);
+    IMFMediaType_Release(media_type);
+
+    hr = IMFActivate_ActivateObject(sink_activate, &IID_IMFMediaSink, (void **)&sink);
+    ok(hr == S_OK, "Failed to activate, hr %#x.\n", hr);
+
+    hr = IMFMediaSink_GetStreamSinkByIndex(sink, 0, &stream_sink);
+    ok(hr == S_OK, "Failed to get stream sink, hr %#x.\n", hr);
+
+    hr = MFCreateTopologyNode(MF_TOPOLOGY_OUTPUT_NODE, &sink_node);
+    ok(hr == S_OK, "Failed to create output node, hr %#x.\n", hr);
+
+    hr = IMFTopologyNode_SetObject(sink_node, (IUnknown *)stream_sink);
+    ok(hr == S_OK, "Failed to set object, hr %#x.\n", hr);
+
+    hr = IMFTopology_AddNode(topology, sink_node);
+    ok(hr == S_OK, "Failed to add sink node, hr %#x.\n", hr);
+
+    hr = IMFTopology_GetNodeCount(topology, &node_count);
+    ok(hr == S_OK, "Failed to get node count, hr %#x.\n", hr);
+    ok(node_count == 4, "Unexpected node count.\n");
+
+    hr = IMFTopoLoader_Load(loader, topology, &full_topology, NULL);
+    ok(hr == S_OK, "Unexpected hr %#x.\n", hr);
+
+    /* unconnected nodes in partial topology are discarded */
+    hr = IMFTopology_GetNodeCount(full_topology, &node_count);
+    ok(hr == S_OK, "Failed to get node count, hr %#x.\n", hr);
+todo_wine
+    ok(node_count == 3, "Unexpected node count %d.\n", node_count);
+
+    IMFTopology_Release(full_topology);
+
+    /* connect nodes for second branch */
+    hr = IMFTopologyNode_ConnectOutput(src_node, 0, sink_node, 0);
+    ok(hr == S_OK, "Failed to connect nodes, hr %#x.\n", hr);
+
+    /* all branches must have valid media types */
+    hr = IMFTopoLoader_Load(loader, topology, &full_topology, NULL);
+todo_wine
+    ok(hr == MF_E_TOPO_CODEC_NOT_FOUND, "Unexpected hr %#x.\n", hr);
+
+    /* set valid media type for second branch */
+    hr = IMFMediaTypeHandler_GetMediaTypeByIndex(mth, 1, &media_type);
+    ok(hr == S_OK, "Failed getting media type, hr %#x.\n", hr);
+
+    hr = IMFMediaTypeHandler_SetCurrentMediaType(mth, media_type);
+    ok(hr == S_OK, "Failed setting current media type, hr %#x.\n", hr);
+
+    hr = IMFTopoLoader_Load(loader, topology, &full_topology, NULL);
+    ok(hr == S_OK, "Failed to resolve topology, hr %#x.\n", hr);
+
+    hr = IMFTopology_GetNodeCount(full_topology, &node_count);
+    ok(hr == S_OK, "Failed to get node count, hr %#x.\n", hr);
+todo_wine
+    ok(node_count == 6, "Unexpected node count %d.\n", node_count);
+
+    IMFTopology_Release(full_topology);
+    IMFMediaType_Release(media_type);
+    IMFTopologyNode_Release(src_node);
+    IMFTopologyNode_Release(sink_node);
+    IMFMediaTypeHandler_Release(mth);
+    IMFStreamSink_Release(stream_sink);
+    IMFMediaSink_Release(sink);
+    IMFActivate_Release(sink_activate);
+    IMFStreamDescriptor_Release(sd);
+
+    IMFPresentationDescriptor_Release(pd);
+    IMFTopology_Release(topology);
     IMFTopoLoader_Release(loader);
 
     hr = MFShutdown();
-- 
2.25.3

From bdaafffdba041b903ec548710a3b0dea223e31f6 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Sergio=20G=C3=B3mez=20Del=20Real?=
 <sdelreal@codeweavers.com>
Date: Wed, 1 Apr 2020 16:11:07 -0500
Subject: [PATCH 04/46] mf: Partially implement the topology loader.
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Signed-off-by: Sergio Gómez Del Real <sdelreal@codeweavers.com>
---
 dlls/mf/tests/mf.c |   3 -
 dlls/mf/topology.c | 337 ++++++++++++++++++++++++++++++++++++++++++---
 2 files changed, 321 insertions(+), 19 deletions(-)

diff --git a/dlls/mf/tests/mf.c b/dlls/mf/tests/mf.c
index 3ce94862c7..46f1dc3c6a 100644
--- a/dlls/mf/tests/mf.c
+++ b/dlls/mf/tests/mf.c
@@ -1483,7 +1483,6 @@ static void test_topology_loader(void)
 
     /* Empty topology */
     hr = IMFTopoLoader_Load(loader, topology, &full_topology, NULL);
-todo_wine
     ok(hr == MF_E_TOPO_UNSUPPORTED, "Unexpected hr %#x.\n", hr);
 
     hr = MFCreateSourceResolver(&resolver);
@@ -1528,7 +1527,6 @@ todo_wine
 
     /* Source node only. */
     hr = IMFTopoLoader_Load(loader, topology, &full_topology, NULL);
-todo_wine
     ok(hr == MF_E_TOPO_UNSUPPORTED, "Unexpected hr %#x.\n", hr);
 
     /* Add grabber sink. */
@@ -1554,7 +1552,6 @@ todo_wine
     ok(hr == S_OK, "Failed to add sink node, hr %#x.\n", hr);
 
     hr = IMFTopoLoader_Load(loader, topology, &full_topology, NULL);
-todo_wine
     ok(hr == MF_E_TOPO_UNSUPPORTED, "Unexpected hr %#x.\n", hr);
 
     hr = IMFTopologyNode_ConnectOutput(src_node, 0, sink_node, 0);
diff --git a/dlls/mf/topology.c b/dlls/mf/topology.c
index 0b4d734442..7a4c95bea0 100644
--- a/dlls/mf/topology.c
+++ b/dlls/mf/topology.c
@@ -1913,47 +1913,352 @@ static ULONG WINAPI topology_loader_Release(IMFTopoLoader *iface)
     return refcount;
 }
 
+static void topology_loader_add_branch(struct topology *topology, IMFTopologyNode *first, IMFTopologyNode *last)
+{
+    IMFTopology *full_topo = &topology->IMFTopology_iface;
+    IMFTopologyNode *in, *out;
+    DWORD index;
+
+    in = first;
+    IMFTopology_AddNode(full_topo, in);
+    while (SUCCEEDED(IMFTopologyNode_GetOutput(in, 0, &out, &index)))
+    {
+        IMFTopology_AddNode(full_topo, out);
+        in = out;
+    }
+}
+
+static HRESULT topology_loader_resolve_branch(IMFTopologyNode *src, IMFMediaType *mediatype, IMFTopologyNode *sink, MF_CONNECT_METHOD method)
+{
+    IMFStreamSink *streamsink;
+    IMFMediaTypeHandler *mth;
+    HRESULT hr;
+
+    IMFTopologyNode_GetObject(sink, (IUnknown **)&streamsink);
+    IMFStreamSink_GetMediaTypeHandler(streamsink, &mth);
+    if (method == MF_CONNECT_DIRECT)
+    {
+        if (FAILED(hr = IMFMediaTypeHandler_SetCurrentMediaType(mth, mediatype)))
+            return hr;
+        hr = IMFTopologyNode_ConnectOutput(src, 0, sink, 0);
+        return hr;
+    }
+    else
+    {
+        IMFTopologyNode *node_dec, *node_conv;
+        GUID major_type, subtype, mft_category;
+        MFT_REGISTER_TYPE_INFO mft_typeinfo;
+        UINT32 flags = MFT_ENUM_FLAG_ALL;
+        IMFActivate **activates_decs;
+        UINT32 num_activates_decs;
+        int i, j;
+
+        IMFMediaType_GetGUID(mediatype, &MF_MT_MAJOR_TYPE, &major_type);
+        if (IsEqualGUID(&major_type, &MFMediaType_Audio))
+            mft_category = MFT_CATEGORY_AUDIO_DECODER;
+        else if (IsEqualGUID(&major_type, &MFMediaType_Video))
+            mft_category = MFT_CATEGORY_VIDEO_DECODER;
+        else
+            return MF_E_INVALIDMEDIATYPE;
+
+        IMFMediaType_GetGUID(mediatype, &MF_MT_SUBTYPE, &subtype);
+        mft_typeinfo.guidMajorType = major_type;
+        mft_typeinfo.guidSubtype = subtype;
+        MFTEnumEx(mft_category, flags, &mft_typeinfo, NULL, &activates_decs, &num_activates_decs);
+
+        /* for getting converters later on */
+        if (IsEqualGUID(&major_type, &MFMediaType_Audio))
+            mft_category = MFT_CATEGORY_AUDIO_EFFECT;
+        else if (IsEqualGUID(&major_type, &MFMediaType_Video))
+            mft_category = MFT_CATEGORY_VIDEO_EFFECT;
+
+        /*
+         * Iterate over number of decoders.
+         * Try to set input type on decoder with source's output media type.
+         * If succeeds, iterate over decoder's output media types.
+         * Try to set input type on sink with decoder's output media type.
+         * If fails, iterate over number of converters.
+         * Try to set input type on converter with decoder's output media type.
+         * If succeeds, iterate over converters output media types.
+         * Try to set input type on sink with converter's output media type.
+         */
+        for (i = 0; i < num_activates_decs; i++)
+        {
+            IMFTransform *decoder;
+
+            IMFActivate_ActivateObject(activates_decs[i], &IID_IMFTransform, (void **)&decoder);
+            if (SUCCEEDED(hr = IMFTransform_SetInputType(decoder, 0, mediatype, 0)))
+            {
+                UINT32 num_activates_convs;
+                IMFActivate **activates_convs;
+                IMFMediaType *decoder_mtype;
+
+                int count = 0;
+                while (SUCCEEDED(IMFTransform_GetOutputAvailableType(decoder, 0, count++, &decoder_mtype)))
+                {
+                    IMFTransform *converter;
+
+                    /* succeeded with source -> decoder -> sink */
+                    if (SUCCEEDED(IMFMediaTypeHandler_SetCurrentMediaType(mth, decoder_mtype)))
+                    {
+                        MFCreateTopologyNode(MF_TOPOLOGY_TRANSFORM_NODE, &node_dec);
+                        IMFTopologyNode_SetObject(node_dec, (IUnknown *)decoder);
+                        IMFTopologyNode_ConnectOutput(src, 0, node_dec, 0);
+                        IMFTopologyNode_ConnectOutput(node_dec, 0, sink, 0);
+
+                        IMFActivate_ShutdownObject(activates_convs[i]);
+                        return S_OK;
+                    }
+
+                    IMFMediaType_GetGUID(decoder_mtype, &MF_MT_SUBTYPE, &subtype);
+                    mft_typeinfo.guidSubtype = subtype;
+                    MFTEnumEx(mft_category, flags, &mft_typeinfo, NULL, &activates_convs, &num_activates_convs);
+                    for (j = 0; j < num_activates_convs; j++)
+                    {
+                        IMFMediaType *converter_mtype;
+
+                        IMFActivate_ActivateObject(activates_convs[j], &IID_IMFTransform, (void **)&converter);
+                        if (SUCCEEDED(IMFTransform_SetInputType(converter, 0, decoder_mtype, 0)))
+                        {
+                            int count = 0;
+                            while (SUCCEEDED(IMFTransform_GetOutputAvailableType(converter, 0, count++, &converter_mtype)))
+                            {
+                                /* succeeded with source -> decoder -> converter -> sink */
+                                if (SUCCEEDED(IMFMediaTypeHandler_SetCurrentMediaType(mth, converter_mtype)))
+                                {
+                                    MFCreateTopologyNode(MF_TOPOLOGY_TRANSFORM_NODE, &node_dec);
+                                    IMFTopologyNode_SetObject(node_dec, (IUnknown *)decoder);
+                                    MFCreateTopologyNode(MF_TOPOLOGY_TRANSFORM_NODE, &node_conv);
+                                    IMFTopologyNode_SetObject(node_conv, (IUnknown *)converter);
+                                    IMFTopologyNode_ConnectOutput(src, 0, node_dec, 0);
+                                    IMFTopologyNode_ConnectOutput(node_dec, 0, node_conv, 0);
+                                    IMFTopologyNode_ConnectOutput(node_conv, 0, sink, 0);
+
+                                    IMFActivate_ShutdownObject(activates_convs[j]);
+                                    IMFActivate_ShutdownObject(activates_decs[i]);
+                                    return S_OK;
+                                }
+                            }
+                        }
+                        IMFActivate_ShutdownObject(activates_convs[j]);
+                    }
+                }
+            }
+            IMFActivate_ShutdownObject(activates_decs[i]);
+        }
+    }
+    return E_FAIL;
+}
+
+static HRESULT topology_loader_resolve_partial_topology(struct topology_node *src, struct topology_node *sink, struct topology *topology, struct topology **full_topology)
+{
+    IMFMediaTypeHandler *mth_src, *mth_sink;
+    IMFTopologyNode *clone_src, *clone_sink;
+    UINT32 method, enum_src_types, streamid;
+    IMFMediaType **src_mediatypes;
+    IMFStreamDescriptor *desc;
+    IMFAttributes *attrs_src;
+    IMFStreamSink *strm_sink;
+    IMFMediaType *mtype_src;
+    DWORD num_media_types;
+    HRESULT hr;
+    int i;
+
+    attrs_src = src->attributes;
+    if (FAILED(hr = IMFAttributes_GetUnknown(attrs_src, &MF_TOPONODE_STREAM_DESCRIPTOR, &IID_IMFStreamDescriptor, (void **)&desc)))
+        return hr;
+    strm_sink = (IMFStreamSink *)sink->object;
+
+    if (FAILED(hr = IMFStreamDescriptor_GetMediaTypeHandler(desc, &mth_src)))
+    {
+        IMFStreamDescriptor_Release(desc);
+        return hr;
+    }
+    if (FAILED(hr = IMFStreamSink_GetMediaTypeHandler(strm_sink, &mth_sink)))
+    {
+        IMFStreamDescriptor_Release(desc);
+        IMFMediaTypeHandler_Release(mth_src);
+        return hr;
+    }
+
+    hr = IMFTopology_GetUINT32(&topology->IMFTopology_iface, &MF_TOPOLOGY_ENUMERATE_SOURCE_TYPES, &enum_src_types);
+
+    mtype_src = NULL;
+    if (FAILED(hr) || !enum_src_types)
+    {
+        num_media_types = 1;
+        enum_src_types = 0;
+        if (FAILED(hr = IMFMediaTypeHandler_GetCurrentMediaType(mth_src, &mtype_src)))
+            if (FAILED(hr = IMFMediaTypeHandler_GetMediaTypeByIndex(mth_src, 0, &mtype_src)))
+            {
+                IMFMediaTypeHandler_Release(mth_src);
+                IMFMediaTypeHandler_Release(mth_sink);
+                IMFStreamDescriptor_Release(desc);
+                return hr;
+            }
+    }
+    else
+        IMFMediaTypeHandler_GetMediaTypeCount(mth_src, &num_media_types);
+
+    src_mediatypes = heap_alloc(sizeof(IMFMediaType *) * num_media_types);
+
+    if (mtype_src)
+        src_mediatypes[0] = mtype_src;
+    else
+        for (i = 0; i < num_media_types; i++)
+            IMFMediaTypeHandler_GetMediaTypeByIndex(mth_src, i, &src_mediatypes[i]);
+
+
+    MFCreateTopologyNode(MF_TOPOLOGY_SOURCESTREAM_NODE, &clone_src);
+    MFCreateTopologyNode(MF_TOPOLOGY_OUTPUT_NODE, &clone_sink);
+    IMFTopologyNode_CloneFrom(clone_src, &src->IMFTopologyNode_iface);
+    IMFTopologyNode_CloneFrom(clone_sink, &sink->IMFTopologyNode_iface);
+
+    if (FAILED(IMFTopologyNode_GetUINT32(clone_sink, &MF_TOPONODE_STREAMID, &streamid)))
+        IMFTopologyNode_SetUINT32(clone_sink, &MF_TOPONODE_STREAMID, 0);
+
+    if (enum_src_types)
+    {
+        hr = IMFAttributes_GetUINT32(attrs_src, &MF_TOPONODE_CONNECT_METHOD, &method);
+        if (hr == S_OK && method != MF_CONNECT_RESOLVE_INDEPENDENT_OUTPUTTYPES)
+        {
+            for (method = MF_CONNECT_DIRECT; method < MF_CONNECT_RESOLVE_INDEPENDENT_OUTPUTTYPES; method++)
+                for (i = 0; i < num_media_types; i++)
+                    if (SUCCEEDED(topology_loader_resolve_branch(clone_src, src_mediatypes[i], clone_sink, method)))
+                    {
+                        topology_loader_add_branch(*full_topology, clone_src, clone_sink);
+                        heap_free(src_mediatypes);
+                        return S_OK;
+                    }
+        }
+        else
+        {
+            for (i = 0; i < num_media_types; i++)
+                for (method = MF_CONNECT_DIRECT; method < MF_CONNECT_RESOLVE_INDEPENDENT_OUTPUTTYPES; method++)
+                    if (SUCCEEDED(topology_loader_resolve_branch(clone_src, src_mediatypes[i], clone_sink, method)))
+                    {
+                        topology_loader_add_branch(*full_topology, clone_src, clone_sink);
+                        heap_free(src_mediatypes);
+                        return S_OK;
+                    }
+        }
+    }
+    else
+    {
+        if (SUCCEEDED(topology_loader_resolve_branch(clone_src, src_mediatypes[0], clone_sink, MF_CONNECT_DIRECT)))
+        {
+            topology_loader_add_branch(*full_topology, clone_src, clone_sink);
+            heap_free(src_mediatypes);
+            return S_OK;
+        }
+    }
+
+    heap_free(src_mediatypes);
+    return MF_E_TOPO_UNSUPPORTED;
+}
+
 static HRESULT WINAPI topology_loader_Load(IMFTopoLoader *iface, IMFTopology *input_topology,
         IMFTopology **output_topology, IMFTopology *current_topology)
 {
     struct topology *topology = unsafe_impl_from_IMFTopology(input_topology);
+    struct topology_node *(*node_pairs)[2];
+    int num_connections;
     IMFStreamSink *sink;
     HRESULT hr;
-    size_t i;
+    int i, idx;
 
     FIXME("%p, %p, %p, %p.\n", iface, input_topology, output_topology, current_topology);
 
     if (current_topology)
         FIXME("Current topology instance is ignored.\n");
 
+    if (!topology || topology->nodes.count < 2)
+        return MF_E_TOPO_UNSUPPORTED;
+
+    num_connections = 0;
+    for (i = 0; i < topology->nodes.count; i++)
+    {
+        struct topology_node *node = topology->nodes.nodes[i];
+
+        if (node->node_type == MF_TOPOLOGY_SOURCESTREAM_NODE)
+        {
+            if (node->outputs.count && node->outputs.streams->connection)
+                num_connections++;
+        }
+    }
+
+    if (!num_connections)
+        return MF_E_TOPO_UNSUPPORTED;
+
+    node_pairs = heap_alloc_zero(sizeof(struct topology_node *[2]) * num_connections);
+
+    idx = 0;
     for (i = 0; i < topology->nodes.count; ++i)
     {
         struct topology_node *node = topology->nodes.nodes[i];
 
-        switch (node->node_type)
+        if (node->node_type == MF_TOPOLOGY_SOURCESTREAM_NODE)
         {
-            case MF_TOPOLOGY_OUTPUT_NODE:
-                if (node->object)
+            if (node->outputs.count && node->outputs.streams->connection)
+            {
+                node_pairs[idx][0] = node;
+                if (node->outputs.streams->connection->node_type == MF_TOPOLOGY_TRANSFORM_NODE)
                 {
-                    /* Sinks must be bound beforehand. */
-                    if (FAILED(IUnknown_QueryInterface(node->object, &IID_IMFStreamSink, (void **)&sink)))
-                        return MF_E_TOPO_SINK_ACTIVATES_UNSUPPORTED;
-                    IMFStreamSink_Release(sink);
+                    struct topology_node *sink = node->outputs.streams->connection;
+
+                    while (sink && sink->node_type != MF_TOPOLOGY_OUTPUT_NODE && sink->outputs.count)
+                        sink = sink->outputs.streams->connection;
+                    if (!sink || !sink->outputs.count)
+                    {
+                        FIXME("Check for MF_CONNECT_AS_OPTIONAL and MF_CONNECT_AS_OPTIONAL_BRANCH flags.\n");
+                        heap_free(node_pairs);
+                        return MF_E_TOPO_UNSUPPORTED;
+                    }
+                    node_pairs[idx][1] = sink;
                 }
-                break;
-            case MF_TOPOLOGY_SOURCESTREAM_NODE:
-                if (FAILED(hr = IMFAttributes_GetItem(node->attributes, &MF_TOPONODE_STREAM_DESCRIPTOR, NULL)))
-                    return hr;
-                break;
-            default:
-                ;
+                else if (node->outputs.streams->connection->node_type == MF_TOPOLOGY_OUTPUT_NODE)
+                    node_pairs[idx][1] = node->outputs.streams->connection;
+                else {
+                    FIXME("Tee nodes currently unhandled.\n");
+                    heap_free(node_pairs);
+                    return MF_E_TOPO_UNSUPPORTED;
+                }
+                idx++;
+            }
         }
     }
 
+    /* all sinks must be activated */
+    for (i = 0; i < num_connections; i++)
+    {
+        if (FAILED(IUnknown_QueryInterface(node_pairs[i][1]->object, &IID_IMFStreamSink, (void **)&sink)))
+        {
+            FIXME("Check for MF_CONNECT_AS_OPTIONAL and MF_CONNECT_AS_OPTIONAL_BRANCH flags.\n");
+            heap_free(node_pairs);
+            return MF_E_TOPO_SINK_ACTIVATES_UNSUPPORTED;
+        }
+        IMFStreamSink_Release(sink);
+    }
+
     if (FAILED(hr = MFCreateTopology(output_topology)))
         return hr;
 
-    return IMFTopology_CloneFrom(*output_topology, input_topology);
+    /* resolve each branch */
+    for (i = 0; i < num_connections; i++)
+    {
+        struct topology_node *src = node_pairs[i][0];
+        struct topology_node *sink = node_pairs[i][1];
+        struct topology *full_topology = unsafe_impl_from_IMFTopology(*output_topology);
+
+        if (FAILED(hr = topology_loader_resolve_partial_topology(src, sink, topology, &full_topology)))
+        {
+            heap_free(node_pairs);
+            return hr;
+        }
+    }
+
+    heap_free(node_pairs);
+    return S_OK;
 }
 
 static const IMFTopoLoaderVtbl topologyloadervtbl =
-- 
2.25.3

From c167aaa99ddadfb239257d7aa99fbc83fa43d3f7 Mon Sep 17 00:00:00 2001
From: Derek Lesho <dlesho@codeweavers.com>
Date: Wed, 25 Mar 2020 10:03:52 -0500
Subject: [PATCH 05/46] Implement stub bytestream handler and source.

Signed-off-by: Derek Lesho <dlesho@codeweavers.com>
---
 dlls/mfplat/tests/mfplat.c                   |   9 +-
 dlls/winegstreamer/Makefile.in               |   1 +
 dlls/winegstreamer/gst_private.h             |   6 +
 dlls/winegstreamer/media_source.c            | 680 +++++++++++++++++++
 dlls/winegstreamer/mfplat.c                  |   9 +
 dlls/winegstreamer/winegstreamer.rgs         |  32 +
 dlls/winegstreamer/winegstreamer_classes.idl |   7 +
 include/mfidl.idl                            |   1 +
 8 files changed, 740 insertions(+), 5 deletions(-)
 create mode 100644 dlls/winegstreamer/media_source.c

diff --git a/dlls/mfplat/tests/mfplat.c b/dlls/mfplat/tests/mfplat.c
index 4bc0813624..7dd9bd54c6 100644
--- a/dlls/mfplat/tests/mfplat.c
+++ b/dlls/mfplat/tests/mfplat.c
@@ -494,7 +494,10 @@ static void test_source_resolver(void)
     ok(obj_type == MF_OBJECT_MEDIASOURCE, "got %d\n", obj_type);
 
     hr = IMFMediaSource_CreatePresentationDescriptor(mediasource, &descriptor);
+todo_wine
     ok(hr == S_OK, "Failed to get presentation descriptor, hr %#x.\n", hr);
+    if (FAILED(hr))
+        goto skip_source_tests;
     ok(descriptor != NULL, "got %p\n", descriptor);
 
     hr = IMFPresentationDescriptor_GetStreamDescriptorByIndex(descriptor, 0, &selected, &sd);
@@ -505,10 +508,7 @@ static void test_source_resolver(void)
     IMFStreamDescriptor_Release(sd);
 
     hr = IMFMediaTypeHandler_GetMajorType(handler, &guid);
-todo_wine
     ok(hr == S_OK, "Failed to get stream major type, hr %#x.\n", hr);
-    if (FAILED(hr))
-        goto skip_source_tests;
 
     /* Check major/minor type for the test media. */
     ok(IsEqualGUID(&guid, &MFMediaType_Video), "Unexpected major type %s.\n", debugstr_guid(&guid));
@@ -589,6 +589,7 @@ todo_wine
     get_event((IMFMediaEventGenerator *)mediasource, MEEndOfPresentation, NULL);
 
     IMFMediaTypeHandler_Release(handler);
+    IMFPresentationDescriptor_Release(descriptor);
 
     hr = IMFMediaSource_Shutdown(mediasource);
     ok(hr == S_OK, "Unexpected hr %#x.\n", hr);
@@ -597,8 +598,6 @@ todo_wine
     ok(hr == MF_E_SHUTDOWN, "Unexpected hr %#x.\n", hr);
 
 skip_source_tests:
-
-    IMFPresentationDescriptor_Release(descriptor);
     IMFMediaSource_Release(mediasource);
     IMFByteStream_Release(stream);
 
diff --git a/dlls/winegstreamer/Makefile.in b/dlls/winegstreamer/Makefile.in
index 337c1086e6..e578d194f7 100644
--- a/dlls/winegstreamer/Makefile.in
+++ b/dlls/winegstreamer/Makefile.in
@@ -10,6 +10,7 @@ C_SRCS = \
 	gst_cbs.c \
 	gstdemux.c \
 	main.c \
+	media_source.c \
 	mediatype.c \
 	mfplat.c \
 	pin.c \
diff --git a/dlls/winegstreamer/gst_private.h b/dlls/winegstreamer/gst_private.h
index e6fb841fc8..71ca429088 100644
--- a/dlls/winegstreamer/gst_private.h
+++ b/dlls/winegstreamer/gst_private.h
@@ -54,4 +54,10 @@ void start_dispatch_thread(void) DECLSPEC_HIDDEN;
 
 extern HRESULT mfplat_get_class_object(REFCLSID rclsid, REFIID riid, void **obj) DECLSPEC_HIDDEN;
 
+enum source_type
+{
+    SOURCE_TYPE_MPEG_4,
+};
+HRESULT container_stream_handler_construct(REFIID riid, void **obj, enum source_type);
+
 #endif /* __GST_PRIVATE_INCLUDED__ */
diff --git a/dlls/winegstreamer/media_source.c b/dlls/winegstreamer/media_source.c
new file mode 100644
index 0000000000..df28f54444
--- /dev/null
+++ b/dlls/winegstreamer/media_source.c
@@ -0,0 +1,680 @@
+#include "gst_private.h"
+
+#include <stdarg.h>
+
+#define COBJMACROS
+#define NONAMELESSUNION
+
+#include "mfapi.h"
+#include "mferror.h"
+#include "mfidl.h"
+
+#include "wine/debug.h"
+#include "wine/heap.h"
+#include "wine/list.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(mfplat);
+
+struct media_source
+{
+    IMFMediaSource IMFMediaSource_iface;
+    LONG ref;
+    IMFMediaEventQueue *event_queue;
+};
+
+static inline struct media_source *impl_from_IMFMediaSource(IMFMediaSource *iface)
+{
+    return CONTAINING_RECORD(iface, struct media_source, IMFMediaSource_iface);
+}
+
+static HRESULT WINAPI media_source_QueryInterface(IMFMediaSource *iface, REFIID riid, void **out)
+{
+    struct media_source *source = impl_from_IMFMediaSource(iface);
+
+    TRACE("(%p)->(%s %p)\n", source, debugstr_guid(riid), out);
+
+    if (IsEqualIID(riid, &IID_IMFMediaSource) ||
+        IsEqualIID(riid, &IID_IMFMediaEventGenerator) ||
+        IsEqualIID(riid, &IID_IUnknown))
+    {
+        *out = &source->IMFMediaSource_iface;
+    }
+    else
+    {
+        FIXME("(%s, %p)\n", debugstr_guid(riid), out);
+        *out = NULL;
+        return E_NOINTERFACE;
+    }
+
+    IUnknown_AddRef((IUnknown*)*out);
+    return S_OK;
+}
+
+static ULONG WINAPI media_source_AddRef(IMFMediaSource *iface)
+{
+    struct media_source *source = impl_from_IMFMediaSource(iface);
+    ULONG ref = InterlockedIncrement(&source->ref);
+
+    TRACE("(%p) ref=%u\n", source, ref);
+
+    return ref;
+}
+
+static ULONG WINAPI media_source_Release(IMFMediaSource *iface)
+{
+    struct media_source *source = impl_from_IMFMediaSource(iface);
+    ULONG ref = InterlockedDecrement(&source->ref);
+
+    TRACE("(%p) ref=%u\n", source, ref);
+
+    if (!ref)
+    {
+        heap_free(source);
+    }
+
+    return ref;
+}
+
+static HRESULT WINAPI media_source_GetEvent(IMFMediaSource *iface, DWORD flags, IMFMediaEvent **event)
+{
+    struct media_source *source = impl_from_IMFMediaSource(iface);
+
+    TRACE("(%p)->(%#x, %p)\n", source, flags, event);
+
+    return IMFMediaEventQueue_GetEvent(source->event_queue, flags, event);
+}
+
+static HRESULT WINAPI media_source_BeginGetEvent(IMFMediaSource *iface, IMFAsyncCallback *callback, IUnknown *state)
+{
+    struct media_source *source = impl_from_IMFMediaSource(iface);
+
+    TRACE("(%p)->(%p, %p)\n", source, callback, state);
+
+    return IMFMediaEventQueue_BeginGetEvent(source->event_queue, callback, state);
+}
+
+static HRESULT WINAPI media_source_EndGetEvent(IMFMediaSource *iface, IMFAsyncResult *result, IMFMediaEvent **event)
+{
+    struct media_source *source = impl_from_IMFMediaSource(iface);
+
+    TRACE("(%p)->(%p, %p)\n", source, result, event);
+
+    return IMFMediaEventQueue_EndGetEvent(source->event_queue, result, event);
+}
+
+static HRESULT WINAPI media_source_QueueEvent(IMFMediaSource *iface, MediaEventType event_type, REFGUID ext_type,
+        HRESULT hr, const PROPVARIANT *value)
+{
+    struct media_source *source = impl_from_IMFMediaSource(iface);
+
+    TRACE("(%p)->(%d, %s, %#x, %p)\n", source, event_type, debugstr_guid(ext_type), hr, value);
+
+    return IMFMediaEventQueue_QueueEventParamVar(source->event_queue, event_type, ext_type, hr, value);
+}
+
+static HRESULT WINAPI media_source_GetCharacteristics(IMFMediaSource *iface, DWORD *characteristics)
+{
+    struct media_source *source = impl_from_IMFMediaSource(iface);
+
+    FIXME("(%p)->(%p): stub\n", source, characteristics);
+
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI media_source_CreatePresentationDescriptor(IMFMediaSource *iface, IMFPresentationDescriptor **descriptor)
+{
+    struct media_source *source = impl_from_IMFMediaSource(iface);
+
+    FIXME("(%p)->(%p): stub\n", source, descriptor);
+
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI media_source_Start(IMFMediaSource *iface, IMFPresentationDescriptor *descriptor,
+                                     const GUID *time_format, const PROPVARIANT *start_position)
+{
+    struct media_source *source = impl_from_IMFMediaSource(iface);
+
+    FIXME("(%p)->(%p, %p, %p): stub\n", source, descriptor, time_format, start_position);
+
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI media_source_Stop(IMFMediaSource *iface)
+{
+    struct media_source *source = impl_from_IMFMediaSource(iface);
+
+    FIXME("(%p): stub\n", source);
+
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI media_source_Pause(IMFMediaSource *iface)
+{
+    struct media_source *source = impl_from_IMFMediaSource(iface);
+
+    FIXME("(%p): stub\n", source);
+
+    return E_NOTIMPL;
+}
+
+static HRESULT media_source_teardown(struct media_source *source)
+{
+    if (source->event_queue)
+        IMFMediaEventQueue_Release(source->event_queue);
+
+    return S_OK;
+}
+
+static HRESULT WINAPI media_source_Shutdown(IMFMediaSource *iface)
+{
+    struct media_source *source = impl_from_IMFMediaSource(iface);
+
+    FIXME("(%p): stub\n", source);
+
+    return E_NOTIMPL;
+}
+
+static const IMFMediaSourceVtbl IMFMediaSource_vtbl =
+{
+    media_source_QueryInterface,
+    media_source_AddRef,
+    media_source_Release,
+    media_source_GetEvent,
+    media_source_BeginGetEvent,
+    media_source_EndGetEvent,
+    media_source_QueueEvent,
+    media_source_GetCharacteristics,
+    media_source_CreatePresentationDescriptor,
+    media_source_Start,
+    media_source_Stop,
+    media_source_Pause,
+    media_source_Shutdown,
+};
+
+static HRESULT media_source_constructor(IMFByteStream *bytestream, enum source_type type, struct media_source **out_media_source)
+{
+    struct media_source *object = heap_alloc_zero(sizeof(*object));
+    HRESULT hr;
+
+    if (!object)
+        return E_OUTOFMEMORY;
+
+    if (FAILED(hr = MFCreateEventQueue(&object->event_queue)))
+        goto fail;
+
+    object->IMFMediaSource_iface.lpVtbl = &IMFMediaSource_vtbl;
+    object->ref = 1;
+
+    *out_media_source = object;
+    return S_OK;
+
+    fail:
+    WARN("Failed to construct MFMediaSource, hr %#x.\n", hr);
+
+    media_source_teardown(object);
+    heap_free(object);
+    return hr;
+}
+
+/* IMFByteStreamHandler */
+
+struct container_stream_handler_result
+{
+    struct list entry;
+    IMFAsyncResult *result;
+    MF_OBJECT_TYPE obj_type;
+    IUnknown *object;
+};
+
+struct container_stream_handler
+{
+    IMFByteStreamHandler IMFByteStreamHandler_iface;
+    IMFAsyncCallback IMFAsyncCallback_iface;
+    LONG refcount;
+    enum source_type type;
+    struct list results;
+    CRITICAL_SECTION cs;
+};
+
+static struct container_stream_handler *impl_from_IMFByteStreamHandler(IMFByteStreamHandler *iface)
+{
+    return CONTAINING_RECORD(iface, struct container_stream_handler, IMFByteStreamHandler_iface);
+}
+
+static struct container_stream_handler *impl_from_IMFAsyncCallback(IMFAsyncCallback *iface)
+{
+    return CONTAINING_RECORD(iface, struct container_stream_handler, IMFAsyncCallback_iface);
+}
+
+static HRESULT WINAPI container_stream_handler_QueryInterface(IMFByteStreamHandler *iface, REFIID riid, void **obj)
+{
+    TRACE("%p, %s, %p.\n", iface, debugstr_guid(riid), obj);
+
+    if (IsEqualIID(riid, &IID_IMFByteStreamHandler) ||
+            IsEqualIID(riid, &IID_IUnknown))
+    {
+        *obj = iface;
+        IMFByteStreamHandler_AddRef(iface);
+        return S_OK;
+    }
+
+    WARN("Unsupported %s.\n", debugstr_guid(riid));
+    *obj = NULL;
+    return E_NOINTERFACE;
+}
+
+static ULONG WINAPI container_stream_handler_AddRef(IMFByteStreamHandler *iface)
+{
+    struct container_stream_handler *handler = impl_from_IMFByteStreamHandler(iface);
+    ULONG refcount = InterlockedIncrement(&handler->refcount);
+
+    TRACE("%p, refcount %u.\n", handler, refcount);
+
+    return refcount;
+}
+
+static ULONG WINAPI container_stream_handler_Release(IMFByteStreamHandler *iface)
+{
+    struct container_stream_handler *handler = impl_from_IMFByteStreamHandler(iface);
+    ULONG refcount = InterlockedDecrement(&handler->refcount);
+    struct container_stream_handler_result *result, *next;
+
+    TRACE("%p, refcount %u.\n", iface, refcount);
+
+    if (!refcount)
+    {
+        LIST_FOR_EACH_ENTRY_SAFE(result, next, &handler->results, struct container_stream_handler_result, entry)
+        {
+            list_remove(&result->entry);
+            IMFAsyncResult_Release(result->result);
+            if (result->object)
+                IUnknown_Release(result->object);
+            heap_free(result);
+        }
+        DeleteCriticalSection(&handler->cs);
+        heap_free(handler);
+    }
+
+    return refcount;
+}
+
+struct create_object_context
+{
+    IUnknown IUnknown_iface;
+    LONG refcount;
+
+    IPropertyStore *props;
+    IMFByteStream *stream;
+    WCHAR *url;
+    DWORD flags;
+};
+
+static struct create_object_context *impl_from_IUnknown(IUnknown *iface)
+{
+    return CONTAINING_RECORD(iface, struct create_object_context, IUnknown_iface);
+}
+
+static HRESULT WINAPI create_object_context_QueryInterface(IUnknown *iface, REFIID riid, void **obj)
+{
+    TRACE("%p, %s, %p.\n", iface, debugstr_guid(riid), obj);
+
+    if (IsEqualIID(riid, &IID_IUnknown))
+    {
+        *obj = iface;
+        IUnknown_AddRef(iface);
+        return S_OK;
+    }
+
+    WARN("Unsupported %s.\n", debugstr_guid(riid));
+    *obj = NULL;
+    return E_NOINTERFACE;
+}
+
+static ULONG WINAPI create_object_context_AddRef(IUnknown *iface)
+{
+    struct create_object_context *context = impl_from_IUnknown(iface);
+    ULONG refcount = InterlockedIncrement(&context->refcount);
+
+    TRACE("%p, refcount %u.\n", iface, refcount);
+
+    return refcount;
+}
+
+static ULONG WINAPI create_object_context_Release(IUnknown *iface)
+{
+    struct create_object_context *context = impl_from_IUnknown(iface);
+    ULONG refcount = InterlockedDecrement(&context->refcount);
+
+    TRACE("%p, refcount %u.\n", iface, refcount);
+
+    if (!refcount)
+    {
+        if (context->props)
+            IPropertyStore_Release(context->props);
+        if (context->stream)
+            IMFByteStream_Release(context->stream);
+        if (context->url)
+            heap_free(context->url);
+        heap_free(context);
+    }
+
+    return refcount;
+}
+
+static const IUnknownVtbl create_object_context_vtbl =
+{
+    create_object_context_QueryInterface,
+    create_object_context_AddRef,
+    create_object_context_Release,
+};
+
+static WCHAR *heap_strdupW(const WCHAR *str)
+{
+    WCHAR *ret = NULL;
+
+    if (str)
+    {
+        unsigned int size;
+
+        size = (lstrlenW(str) + 1) * sizeof(WCHAR);
+        ret = heap_alloc(size);
+        if (ret)
+            memcpy(ret, str, size);
+    }
+
+    return ret;
+}
+
+static HRESULT WINAPI container_stream_handler_BeginCreateObject(IMFByteStreamHandler *iface, IMFByteStream *stream, const WCHAR *url, DWORD flags,
+        IPropertyStore *props, IUnknown **cancel_cookie, IMFAsyncCallback *callback, IUnknown *state)
+{
+    struct container_stream_handler *this = impl_from_IMFByteStreamHandler(iface);
+    struct create_object_context *context;
+    IMFAsyncResult *caller, *item;
+    HRESULT hr;
+
+    TRACE("%p, %s, %#x, %p, %p, %p, %p.\n", iface, debugstr_w(url), flags, props, cancel_cookie, callback, state);
+
+    if (cancel_cookie)
+        *cancel_cookie = NULL;
+
+    if (FAILED(hr = MFCreateAsyncResult(NULL, callback, state, &caller)))
+        return hr;
+
+    context = heap_alloc(sizeof(*context));
+    if (!context)
+    {
+        IMFAsyncResult_Release(caller);
+        return E_OUTOFMEMORY;
+    }
+
+    context->IUnknown_iface.lpVtbl = &create_object_context_vtbl;
+    context->refcount = 1;
+    context->props = props;
+    if (context->props)
+        IPropertyStore_AddRef(context->props);
+    context->flags = flags;
+    context->stream = stream;
+    if (context->stream)
+        IMFByteStream_AddRef(context->stream);
+    if (url)
+        context->url = heap_strdupW(url);
+    if (!context->stream)
+    {
+        IMFAsyncResult_Release(caller);
+        IUnknown_Release(&context->IUnknown_iface);
+        return E_OUTOFMEMORY;
+    }
+
+    hr = MFCreateAsyncResult(&context->IUnknown_iface, &this->IMFAsyncCallback_iface, (IUnknown *)caller, &item);
+    IUnknown_Release(&context->IUnknown_iface);
+    if (SUCCEEDED(hr))
+    {
+        if (SUCCEEDED(hr = MFPutWorkItemEx(MFASYNC_CALLBACK_QUEUE_IO, item)))
+        {
+            if (cancel_cookie)
+            {
+                *cancel_cookie = (IUnknown *)caller;
+                IUnknown_AddRef(*cancel_cookie);
+            }
+        }
+
+        IMFAsyncResult_Release(item);
+    }
+    IMFAsyncResult_Release(caller);
+
+    return hr;
+}
+
+static HRESULT WINAPI container_stream_handler_EndCreateObject(IMFByteStreamHandler *iface, IMFAsyncResult *result,
+        MF_OBJECT_TYPE *obj_type, IUnknown **object)
+{
+    struct container_stream_handler *this = impl_from_IMFByteStreamHandler(iface);
+    struct container_stream_handler_result *found = NULL, *cur;
+    HRESULT hr;
+
+    TRACE("%p, %p, %p, %p.\n", iface, result, obj_type, object);
+
+    EnterCriticalSection(&this->cs);
+
+    LIST_FOR_EACH_ENTRY(cur, &this->results, struct container_stream_handler_result, entry)
+    {
+        if (result == cur->result)
+        {
+            list_remove(&cur->entry);
+            found = cur;
+            break;
+        }
+    }
+
+    LeaveCriticalSection(&this->cs);
+
+    if (found)
+    {
+        *obj_type = found->obj_type;
+        *object = found->object;
+        hr = IMFAsyncResult_GetStatus(found->result);
+        IMFAsyncResult_Release(found->result);
+        heap_free(found);
+    }
+    else
+    {
+        *obj_type = MF_OBJECT_INVALID;
+        *object = NULL;
+        hr = MF_E_UNEXPECTED;
+    }
+
+    return hr;
+}
+
+static HRESULT WINAPI container_stream_handler_CancelObjectCreation(IMFByteStreamHandler *iface, IUnknown *cancel_cookie)
+{
+    struct container_stream_handler *this = impl_from_IMFByteStreamHandler(iface);
+    struct container_stream_handler_result *found = NULL, *cur;
+
+    TRACE("%p, %p.\n", iface, cancel_cookie);
+
+    EnterCriticalSection(&this->cs);
+
+    LIST_FOR_EACH_ENTRY(cur, &this->results, struct container_stream_handler_result, entry)
+    {
+        if (cancel_cookie == (IUnknown *)cur->result)
+        {
+            list_remove(&cur->entry);
+            found = cur;
+            break;
+        }
+    }
+
+    LeaveCriticalSection(&this->cs);
+
+    if (found)
+    {
+        IMFAsyncResult_Release(found->result);
+        if (found->object)
+            IUnknown_Release(found->object);
+        heap_free(found);
+    }
+
+    return found ? S_OK : MF_E_UNEXPECTED;
+}
+
+static HRESULT WINAPI container_stream_handler_GetMaxNumberOfBytesRequiredForResolution(IMFByteStreamHandler *iface, QWORD *bytes)
+{
+    FIXME("stub (%p %p)\n", iface, bytes);
+    return E_NOTIMPL;
+}
+
+static const IMFByteStreamHandlerVtbl container_stream_handler_vtbl =
+{
+    container_stream_handler_QueryInterface,
+    container_stream_handler_AddRef,
+    container_stream_handler_Release,
+    container_stream_handler_BeginCreateObject,
+    container_stream_handler_EndCreateObject,
+    container_stream_handler_CancelObjectCreation,
+    container_stream_handler_GetMaxNumberOfBytesRequiredForResolution,
+};
+
+static HRESULT WINAPI container_stream_handler_callback_QueryInterface(IMFAsyncCallback *iface, REFIID riid, void **obj)
+{
+    if (IsEqualIID(riid, &IID_IMFAsyncCallback) ||
+            IsEqualIID(riid, &IID_IUnknown))
+    {
+        *obj = iface;
+        IMFAsyncCallback_AddRef(iface);
+        return S_OK;
+    }
+
+    WARN("Unsupported %s.\n", debugstr_guid(riid));
+    *obj = NULL;
+    return E_NOINTERFACE;
+}
+
+static ULONG WINAPI container_stream_handler_callback_AddRef(IMFAsyncCallback *iface)
+{
+    struct container_stream_handler *handler = impl_from_IMFAsyncCallback(iface);
+    return IMFByteStreamHandler_AddRef(&handler->IMFByteStreamHandler_iface);
+}
+
+static ULONG WINAPI container_stream_handler_callback_Release(IMFAsyncCallback *iface)
+{
+    struct container_stream_handler *handler = impl_from_IMFAsyncCallback(iface);
+    return IMFByteStreamHandler_Release(&handler->IMFByteStreamHandler_iface);
+}
+
+static HRESULT WINAPI container_stream_handler_callback_GetParameters(IMFAsyncCallback *iface, DWORD *flags, DWORD *queue)
+{
+    return E_NOTIMPL;
+}
+
+static HRESULT container_stream_handler_create_object(struct container_stream_handler *This, WCHAR *url, IMFByteStream *stream, DWORD flags,
+                                            IPropertyStore *props, IUnknown **out_object, MF_OBJECT_TYPE *out_obj_type)
+{
+    TRACE("(%p %s %p %u %p %p %p)\n", This, debugstr_w(url), stream, flags, props, out_object, out_obj_type);
+
+    if (flags & MF_RESOLUTION_MEDIASOURCE)
+    {
+        HRESULT hr;
+        struct media_source *new_source;
+
+        if (FAILED(hr = media_source_constructor(stream, This->type, &new_source)))
+            return hr;
+
+        TRACE("->(%p)\n", new_source);
+
+        *out_object = (IUnknown*)&new_source->IMFMediaSource_iface;
+        *out_obj_type = MF_OBJECT_MEDIASOURCE;
+
+        return S_OK;
+    }
+    else
+    {
+        FIXME("flags = %08x\n", flags);
+        return E_NOTIMPL;
+    }
+}
+
+static HRESULT WINAPI container_stream_handler_callback_Invoke(IMFAsyncCallback *iface, IMFAsyncResult *result)
+{
+    struct container_stream_handler *handler = impl_from_IMFAsyncCallback(iface);
+    struct container_stream_handler_result *handler_result;
+    MF_OBJECT_TYPE obj_type = MF_OBJECT_INVALID;
+    IUnknown *object = NULL, *context_object;
+    struct create_object_context *context;
+    IMFAsyncResult *caller;
+    HRESULT hr;
+
+    caller = (IMFAsyncResult *)IMFAsyncResult_GetStateNoAddRef(result);
+
+    if (FAILED(hr = IMFAsyncResult_GetObject(result, &context_object)))
+    {
+        WARN("Expected context set for callee result.\n");
+        return hr;
+    }
+
+    context = impl_from_IUnknown(context_object);
+
+    hr = container_stream_handler_create_object(handler, context->url, context->stream, context->flags, context->props, &object, &obj_type);
+
+    handler_result = heap_alloc(sizeof(*handler_result));
+    if (handler_result)
+    {
+        handler_result->result = caller;
+        IMFAsyncResult_AddRef(handler_result->result);
+        handler_result->obj_type = obj_type;
+        handler_result->object = object;
+
+        EnterCriticalSection(&handler->cs);
+        list_add_tail(&handler->results, &handler_result->entry);
+        LeaveCriticalSection(&handler->cs);
+    }
+    else
+    {
+        if (object)
+            IUnknown_Release(object);
+        hr = E_OUTOFMEMORY;
+    }
+
+    IUnknown_Release(&context->IUnknown_iface);
+
+    IMFAsyncResult_SetStatus(caller, hr);
+    MFInvokeCallback(caller);
+
+    return S_OK;
+}
+
+static const IMFAsyncCallbackVtbl container_stream_handler_callback_vtbl =
+{
+    container_stream_handler_callback_QueryInterface,
+    container_stream_handler_callback_AddRef,
+    container_stream_handler_callback_Release,
+    container_stream_handler_callback_GetParameters,
+    container_stream_handler_callback_Invoke,
+};
+
+HRESULT container_stream_handler_construct(REFIID riid, void **obj, enum source_type type)
+{
+    struct container_stream_handler *this;
+    HRESULT hr;
+
+    TRACE("%s, %p.\n", debugstr_guid(riid), obj);
+
+    this = heap_alloc_zero(sizeof(*this));
+    if (!this)
+        return E_OUTOFMEMORY;
+
+    list_init(&this->results);
+    InitializeCriticalSection(&this->cs);
+
+    this->type = type;
+    this->IMFByteStreamHandler_iface.lpVtbl = &container_stream_handler_vtbl;
+    this->IMFAsyncCallback_iface.lpVtbl = &container_stream_handler_callback_vtbl;
+    this->refcount = 1;
+
+    hr = IMFByteStreamHandler_QueryInterface(&this->IMFByteStreamHandler_iface, riid, obj);
+    IMFByteStreamHandler_Release(&this->IMFByteStreamHandler_iface);
+
+    return hr;
+}
\ No newline at end of file
diff --git a/dlls/winegstreamer/mfplat.c b/dlls/winegstreamer/mfplat.c
index 55b9b08876..16e55247de 100644
--- a/dlls/winegstreamer/mfplat.c
+++ b/dlls/winegstreamer/mfplat.c
@@ -398,6 +398,11 @@ failed:
     return hr;
 }
 
+static HRESULT mp4_stream_handler_create(REFIID riid, void **ret)
+{
+    return container_stream_handler_construct(riid, ret, SOURCE_TYPE_MPEG_4);
+}
+
 static const struct class_object
 {
     const GUID *clsid;
@@ -406,6 +411,7 @@ static const struct class_object
 class_objects[] =
 {
     { &CLSID_VideoProcessorMFT, &video_processor_create },
+    { &CLSID_MPEG4ByteStreamHandler, &mp4_stream_handler_create },
 };
 
 HRESULT mfplat_get_class_object(REFCLSID rclsid, REFIID riid, void **obj)
@@ -414,6 +420,9 @@ HRESULT mfplat_get_class_object(REFCLSID rclsid, REFIID riid, void **obj)
     unsigned int i;
     HRESULT hr;
 
+    if (!(init_gstreamer()))
+        return CLASS_E_CLASSNOTAVAILABLE;
+
     for (i = 0; i < ARRAY_SIZE(class_objects); ++i)
     {
         if (IsEqualGUID(class_objects[i].clsid, rclsid))
diff --git a/dlls/winegstreamer/winegstreamer.rgs b/dlls/winegstreamer/winegstreamer.rgs
index 923ba673f8..0bc26761e9 100644
--- a/dlls/winegstreamer/winegstreamer.rgs
+++ b/dlls/winegstreamer/winegstreamer.rgs
@@ -12,3 +12,35 @@ HKCR
         }
     }
 }
+
+HKLM
+{
+    NoRemove 'Software'
+    {
+        NoRemove 'Microsoft'
+        {
+            NoRemove 'Windows Media Foundation'
+            {
+                NoRemove 'ByteStreamHandlers'
+                {
+                    '.m4v'
+                    {
+                        val '{271C3902-6095-4c45-A22F-20091816EE9E}' = s 'MPEG4 Byte Stream Handler'
+                    }
+                    '.mp4'
+                    {
+                        val '{271C3902-6095-4c45-A22F-20091816EE9E}' = s 'MPEG4 Byte Stream Handler'
+                    }
+                    'video/m4v'
+                    {
+                        val '{271C3902-6095-4c45-A22F-20091816EE9E}' = s 'MPEG4 Byte Stream Handler'
+                    }
+                    'video/mp4'
+                    {
+                        val '{271C3902-6095-4c45-A22F-20091816EE9E}' = s 'MPEG4 Byte Stream Handler'
+                    }
+                }
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/dlls/winegstreamer/winegstreamer_classes.idl b/dlls/winegstreamer/winegstreamer_classes.idl
index fa0e178405..997a28b052 100644
--- a/dlls/winegstreamer/winegstreamer_classes.idl
+++ b/dlls/winegstreamer/winegstreamer_classes.idl
@@ -54,3 +54,10 @@ coclass Gstreamer_Splitter {}
     uuid(88753b26-5b24-49bd-b2e7-0c445c78c982)
 ]
 coclass VideoProcessorMFT {}
+
+[
+    helpstring("MP4 Byte Stream Handler"),
+    threading(both),
+    uuid(271c3902-6095-4c45-a22f-20091816ee9e)
+]
+coclass MPEG4ByteStreamHandler {}
diff --git a/include/mfidl.idl b/include/mfidl.idl
index 4e7a43f5ff..c61ccb788c 100644
--- a/include/mfidl.idl
+++ b/include/mfidl.idl
@@ -1128,3 +1128,4 @@ cpp_quote("EXTERN_GUID(MF_AUDIO_RENDERER_ATTRIBUTE_ENDPOINT_ROLE, 0x6ba644ff, 0x
 cpp_quote("EXTERN_GUID(MF_AUDIO_RENDERER_ATTRIBUTE_STREAM_CATEGORY, 0xa9770471, 0x92ec, 0x4df4, 0x94, 0xfe, 0x81, 0xc3, 0x6f, 0x0c, 0x3a, 0x7a);")
 
 cpp_quote("EXTERN_GUID(CLSID_VideoProcessorMFT, 0x88753b26, 0x5b24, 0x49bd, 0xb2, 0xe7, 0xc, 0x44, 0x5c, 0x78, 0xc9, 0x82);")
+cpp_quote("EXTERN_GUID(CLSID_MPEG4ByteStreamHandler, 0x271c3902, 0x6095, 0x4c45, 0xa2, 0x2f, 0x20, 0x09, 0x18, 0x16, 0xee, 0x9e);")
\ No newline at end of file
-- 
2.25.3

From ca28f51b1e8a5127ef387be46009bc594bcc7eba Mon Sep 17 00:00:00 2001
From: Derek Lesho <dlesho@codeweavers.com>
Date: Mon, 30 Mar 2020 15:19:01 -0500
Subject: [PATCH 06/46] winegstreamer: Implement IMFMediaSource::Shutdown.

Signed-off-by: Derek Lesho <dlesho@codeweavers.com>
---
 dlls/mfplat/tests/mfplat.c        |  2 +-
 dlls/winegstreamer/media_source.c | 39 +++++++++++++++++++++++++++++--
 2 files changed, 38 insertions(+), 3 deletions(-)

diff --git a/dlls/mfplat/tests/mfplat.c b/dlls/mfplat/tests/mfplat.c
index 7dd9bd54c6..6381a93bd1 100644
--- a/dlls/mfplat/tests/mfplat.c
+++ b/dlls/mfplat/tests/mfplat.c
@@ -591,13 +591,13 @@ todo_wine
     IMFMediaTypeHandler_Release(handler);
     IMFPresentationDescriptor_Release(descriptor);
 
+skip_source_tests:
     hr = IMFMediaSource_Shutdown(mediasource);
     ok(hr == S_OK, "Unexpected hr %#x.\n", hr);
 
     hr = IMFMediaSource_CreatePresentationDescriptor(mediasource, NULL);
     ok(hr == MF_E_SHUTDOWN, "Unexpected hr %#x.\n", hr);
 
-skip_source_tests:
     IMFMediaSource_Release(mediasource);
     IMFByteStream_Release(stream);
 
diff --git a/dlls/winegstreamer/media_source.c b/dlls/winegstreamer/media_source.c
index df28f54444..09a40a0620 100644
--- a/dlls/winegstreamer/media_source.c
+++ b/dlls/winegstreamer/media_source.c
@@ -20,6 +20,14 @@ struct media_source
     IMFMediaSource IMFMediaSource_iface;
     LONG ref;
     IMFMediaEventQueue *event_queue;
+    enum
+    {
+        SOURCE_OPENING,
+        SOURCE_STOPPED, /* (READY) */
+        SOURCE_PAUSED,
+        SOURCE_RUNNING,
+        SOURCE_SHUTDOWN,
+    } state;
 };
 
 static inline struct media_source *impl_from_IMFMediaSource(IMFMediaSource *iface)
@@ -81,6 +89,9 @@ static HRESULT WINAPI media_source_GetEvent(IMFMediaSource *iface, DWORD flags,
 
     TRACE("(%p)->(%#x, %p)\n", source, flags, event);
 
+    if (source->state == SOURCE_SHUTDOWN)
+        return MF_E_SHUTDOWN;
+
     return IMFMediaEventQueue_GetEvent(source->event_queue, flags, event);
 }
 
@@ -90,6 +101,9 @@ static HRESULT WINAPI media_source_BeginGetEvent(IMFMediaSource *iface, IMFAsync
 
     TRACE("(%p)->(%p, %p)\n", source, callback, state);
 
+    if (source->state == SOURCE_SHUTDOWN)
+        return MF_E_SHUTDOWN;
+
     return IMFMediaEventQueue_BeginGetEvent(source->event_queue, callback, state);
 }
 
@@ -99,6 +113,9 @@ static HRESULT WINAPI media_source_EndGetEvent(IMFMediaSource *iface, IMFAsyncRe
 
     TRACE("(%p)->(%p, %p)\n", source, result, event);
 
+    if (source->state == SOURCE_SHUTDOWN)
+        return MF_E_SHUTDOWN;
+
     return IMFMediaEventQueue_EndGetEvent(source->event_queue, result, event);
 }
 
@@ -109,6 +126,9 @@ static HRESULT WINAPI media_source_QueueEvent(IMFMediaSource *iface, MediaEventT
 
     TRACE("(%p)->(%d, %s, %#x, %p)\n", source, event_type, debugstr_guid(ext_type), hr, value);
 
+    if (source->state == SOURCE_SHUTDOWN)
+        return MF_E_SHUTDOWN;
+
     return IMFMediaEventQueue_QueueEventParamVar(source->event_queue, event_type, ext_type, hr, value);
 }
 
@@ -118,6 +138,9 @@ static HRESULT WINAPI media_source_GetCharacteristics(IMFMediaSource *iface, DWO
 
     FIXME("(%p)->(%p): stub\n", source, characteristics);
 
+    if (source->state == SOURCE_SHUTDOWN)
+        return MF_E_SHUTDOWN;
+
     return E_NOTIMPL;
 }
 
@@ -127,6 +150,9 @@ static HRESULT WINAPI media_source_CreatePresentationDescriptor(IMFMediaSource *
 
     FIXME("(%p)->(%p): stub\n", source, descriptor);
 
+    if (source->state == SOURCE_SHUTDOWN)
+        return MF_E_SHUTDOWN;
+
     return E_NOTIMPL;
 }
 
@@ -137,6 +163,9 @@ static HRESULT WINAPI media_source_Start(IMFMediaSource *iface, IMFPresentationD
 
     FIXME("(%p)->(%p, %p, %p): stub\n", source, descriptor, time_format, start_position);
 
+    if (source->state == SOURCE_SHUTDOWN)
+        return MF_E_SHUTDOWN;
+
     return E_NOTIMPL;
 }
 
@@ -146,6 +175,9 @@ static HRESULT WINAPI media_source_Stop(IMFMediaSource *iface)
 
     FIXME("(%p): stub\n", source);
 
+    if (source->state == SOURCE_SHUTDOWN)
+        return MF_E_SHUTDOWN;
+
     return E_NOTIMPL;
 }
 
@@ -170,9 +202,10 @@ static HRESULT WINAPI media_source_Shutdown(IMFMediaSource *iface)
 {
     struct media_source *source = impl_from_IMFMediaSource(iface);
 
-    FIXME("(%p): stub\n", source);
+    TRACE("(%p)\n", source);
 
-    return E_NOTIMPL;
+    source->state = SOURCE_SHUTDOWN;
+    return media_source_teardown(source);
 }
 
 static const IMFMediaSourceVtbl IMFMediaSource_vtbl =
@@ -203,6 +236,8 @@ static HRESULT media_source_constructor(IMFByteStream *bytestream, enum source_t
     if (FAILED(hr = MFCreateEventQueue(&object->event_queue)))
         goto fail;
 
+    object->state = SOURCE_STOPPED;
+
     object->IMFMediaSource_iface.lpVtbl = &IMFMediaSource_vtbl;
     object->ref = 1;
 
-- 
2.25.3

From 4fd112ab389a92f3dc98a8d4b7370909c9511b45 Mon Sep 17 00:00:00 2001
From: Derek Lesho <dlesho@codeweavers.com>
Date: Mon, 30 Mar 2020 14:19:35 -0500
Subject: [PATCH 07/46] winegstreamer: Add a GstPad wrapping the media source's
 bytestream.

Signed-off-by: Derek Lesho <dlesho@codeweavers.com>
---
 dlls/winegstreamer/gst_cbs.c      |  58 ++++++++
 dlls/winegstreamer/gst_cbs.h      |  12 +-
 dlls/winegstreamer/media_source.c | 237 +++++++++++++++++++++++++++++-
 3 files changed, 302 insertions(+), 5 deletions(-)

diff --git a/dlls/winegstreamer/gst_cbs.c b/dlls/winegstreamer/gst_cbs.c
index bf7103b160..141ab3c611 100644
--- a/dlls/winegstreamer/gst_cbs.c
+++ b/dlls/winegstreamer/gst_cbs.c
@@ -49,6 +49,8 @@ static void CALLBACK perform_cb(TP_CALLBACK_INSTANCE *instance, void *user)
 
     if (cbdata->type < GSTDEMUX_MAX)
         perform_cb_gstdemux(cbdata);
+    else if (cbdata->type < MEDIA_SOURCE_MAX)
+        perform_cb_media_source(cbdata);
 
     pthread_mutex_lock(&cbdata->lock);
     cbdata->finished = 1;
@@ -301,3 +303,59 @@ gboolean query_sink_wrapper(GstPad *pad, GstObject *parent, GstQuery *query)
 
     return cbdata.u.query_sink_data.ret;
 }
+
+GstFlowReturn pull_from_bytestream_wrapper(GstPad *pad, GstObject *parent, guint64 ofs, guint len,
+        GstBuffer **buf)
+{
+    struct cb_data cbdata = { PULL_FROM_BYTESTREAM };
+
+    cbdata.u.getrange_data.pad = pad;
+    cbdata.u.getrange_data.parent = parent;
+    cbdata.u.getrange_data.ofs = ofs;
+    cbdata.u.getrange_data.len = len;
+    cbdata.u.getrange_data.buf = buf;
+
+    call_cb(&cbdata);
+
+    return cbdata.u.getrange_data.ret;
+}
+
+gboolean query_bytestream_wrapper(GstPad *pad, GstObject *parent, GstQuery *query)
+{
+    struct cb_data cbdata = { QUERY_BYTESTREAM };
+
+    cbdata.u.query_function_data.pad = pad;
+    cbdata.u.query_function_data.parent = parent;
+    cbdata.u.query_function_data.query = query;
+
+    call_cb(&cbdata);
+
+    return cbdata.u.query_function_data.ret;
+}
+
+gboolean activate_bytestream_pad_mode_wrapper(GstPad *pad, GstObject *parent, GstPadMode mode, gboolean activate)
+{
+    struct cb_data cbdata = { ACTIVATE_BYTESTREAM_PAD_MODE };
+
+    cbdata.u.activate_mode_data.pad = pad;
+    cbdata.u.activate_mode_data.parent = parent;
+    cbdata.u.activate_mode_data.mode = mode;
+    cbdata.u.activate_mode_data.activate = activate;
+
+    call_cb(&cbdata);
+
+    return cbdata.u.query_function_data.ret;
+}
+
+gboolean process_bytestream_pad_event_wrapper(GstPad *pad, GstObject *parent, GstEvent *event)
+{
+    struct cb_data cbdata = { PROCESS_BYTESTREAM_PAD_EVENT };
+
+    cbdata.u.event_src_data.pad = pad;
+    cbdata.u.event_src_data.parent = parent;
+    cbdata.u.event_src_data.event = event;
+
+    call_cb(&cbdata);
+
+    return cbdata.u.event_src_data.ret;
+}
\ No newline at end of file
diff --git a/dlls/winegstreamer/gst_cbs.h b/dlls/winegstreamer/gst_cbs.h
index 4725f23ad1..10e999feea 100644
--- a/dlls/winegstreamer/gst_cbs.h
+++ b/dlls/winegstreamer/gst_cbs.h
@@ -43,7 +43,12 @@ enum CB_TYPE {
     AUTOPLUG_BLACKLIST,
     UNKNOWN_TYPE,
     QUERY_SINK,
-    GSTDEMUX_MAX
+    GSTDEMUX_MAX,
+    PULL_FROM_BYTESTREAM,
+    QUERY_BYTESTREAM,
+    ACTIVATE_BYTESTREAM_PAD_MODE,
+    PROCESS_BYTESTREAM_PAD_EVENT,
+    MEDIA_SOURCE_MAX,
 };
 
 struct cb_data {
@@ -138,6 +143,7 @@ struct cb_data {
 
 void mark_wine_thread(void) DECLSPEC_HIDDEN;
 void perform_cb_gstdemux(struct cb_data *data) DECLSPEC_HIDDEN;
+void perform_cb_media_source(struct cb_data *data) DECLSPEC_HIDDEN;
 
 GstBusSyncReply watch_bus_wrapper(GstBus *bus, GstMessage *msg, gpointer user) DECLSPEC_HIDDEN;
 void existing_new_pad_wrapper(GstElement *bin, GstPad *pad, gpointer user) DECLSPEC_HIDDEN;
@@ -154,5 +160,9 @@ GstAutoplugSelectResult autoplug_blacklist_wrapper(GstElement *bin, GstPad *pad,
 void unknown_type_wrapper(GstElement *bin, GstPad *pad, GstCaps *caps, gpointer user) DECLSPEC_HIDDEN;
 void Gstreamer_transform_pad_added_wrapper(GstElement *filter, GstPad *pad, gpointer user) DECLSPEC_HIDDEN;
 gboolean query_sink_wrapper(GstPad *pad, GstObject *parent, GstQuery *query) DECLSPEC_HIDDEN;
+GstFlowReturn pull_from_bytestream_wrapper(GstPad *pad, GstObject *parent, guint64 ofs, guint len, GstBuffer **buf) DECLSPEC_HIDDEN;
+gboolean query_bytestream_wrapper(GstPad *pad, GstObject *parent, GstQuery *query) DECLSPEC_HIDDEN;
+gboolean activate_bytestream_pad_mode_wrapper(GstPad *pad, GstObject *parent, GstPadMode mode, gboolean activate) DECLSPEC_HIDDEN;
+gboolean process_bytestream_pad_event_wrapper(GstPad *pad, GstObject *parent, GstEvent *event) DECLSPEC_HIDDEN;
 
 #endif
diff --git a/dlls/winegstreamer/media_source.c b/dlls/winegstreamer/media_source.c
index 09a40a0620..48119e4a89 100644
--- a/dlls/winegstreamer/media_source.c
+++ b/dlls/winegstreamer/media_source.c
@@ -1,4 +1,9 @@
+#include "config.h"
+
+#include <gst/gst.h>
+
 #include "gst_private.h"
+#include "gst_cbs.h"
 
 #include <stdarg.h>
 
@@ -8,6 +13,7 @@
 #include "mfapi.h"
 #include "mferror.h"
 #include "mfidl.h"
+#include "mfobjects.h"
 
 #include "wine/debug.h"
 #include "wine/heap.h"
@@ -15,11 +21,24 @@
 
 WINE_DEFAULT_DEBUG_CHANNEL(mfplat);
 
+static struct source_desc
+{
+    GstStaticCaps bytestream_caps;
+} source_descs[] =
+{
+    {/*SOURCE_TYPE_MPEG_4*/
+        GST_STATIC_CAPS("video/quicktime"),
+    }
+};
+
 struct media_source
 {
     IMFMediaSource IMFMediaSource_iface;
     LONG ref;
+    enum source_type type;
     IMFMediaEventQueue *event_queue;
+    IMFByteStream *byte_stream;
+    GstPad *my_src;
     enum
     {
         SOURCE_OPENING,
@@ -35,6 +54,154 @@ static inline struct media_source *impl_from_IMFMediaSource(IMFMediaSource *ifac
     return CONTAINING_RECORD(iface, struct media_source, IMFMediaSource_iface);
 }
 
+GstFlowReturn pull_from_bytestream(GstPad *pad, GstObject *parent, guint64 ofs, guint len,
+        GstBuffer **buf)
+{
+    struct media_source *source = gst_pad_get_element_private(pad);
+    IMFByteStream *byte_stream = source->byte_stream;
+    ULONG bytes_read;
+    GstMapInfo info;
+    BOOL is_eof;
+    HRESULT hr;
+
+    TRACE("gstreamer requesting %u bytes at %s from source %p into buffer %p\n", len, wine_dbgstr_longlong(ofs), source, buf);
+
+    if (ofs != GST_BUFFER_OFFSET_NONE)
+    {
+        if (FAILED(IMFByteStream_SetCurrentPosition(byte_stream, ofs)))
+            return GST_FLOW_ERROR;
+    }
+
+    if (FAILED(IMFByteStream_IsEndOfStream(byte_stream, &is_eof)))
+        return GST_FLOW_ERROR;
+    if (is_eof)
+        return GST_FLOW_EOS;
+
+    if (!(*buf))
+        *buf = gst_buffer_new_and_alloc(len);
+    gst_buffer_map(*buf, &info, GST_MAP_WRITE);
+    hr = IMFByteStream_Read(byte_stream, info.data, len, &bytes_read);
+    gst_buffer_unmap(*buf, &info);
+
+    gst_buffer_set_size(*buf, bytes_read);
+
+    if (FAILED(hr))
+    {
+        return GST_FLOW_ERROR;
+    }
+    GST_BUFFER_OFFSET(*buf) = ofs;
+    return GST_FLOW_OK;
+}
+
+static gboolean query_bytestream(GstPad *pad, GstObject *parent, GstQuery *query)
+{
+    struct media_source *source = gst_pad_get_element_private(pad);
+    GstFormat format;
+    QWORD bytestream_len;
+
+    TRACE("GStreamer queries source %p for %s\n", source, GST_QUERY_TYPE_NAME(query));
+
+    if (FAILED(IMFByteStream_GetLength(source->byte_stream, &bytestream_len)))
+        return FALSE;
+
+    switch (GST_QUERY_TYPE(query))
+    {
+        case GST_QUERY_DURATION:
+        {
+            gst_query_parse_duration (query, &format, NULL);
+            if (format == GST_FORMAT_PERCENT) {
+                gst_query_set_duration (query, GST_FORMAT_PERCENT, GST_FORMAT_PERCENT_MAX);
+                return TRUE;
+            }
+            else if (format == GST_FORMAT_BYTES)
+            {
+                QWORD length;
+                IMFByteStream_GetLength(source->byte_stream, &length);
+                gst_query_set_duration (query, GST_FORMAT_BYTES, length);
+                return TRUE;
+            }
+            return FALSE;
+        }
+        case GST_QUERY_SEEKING:
+        {
+            gst_query_parse_seeking (query, &format, NULL, NULL, NULL);
+            if (format != GST_FORMAT_BYTES)
+            {
+                WARN("Cannot seek using format \"%s\".\n", gst_format_get_name(format));
+                return FALSE;
+            }
+            gst_query_set_seeking(query, GST_FORMAT_BYTES, 1, 0, bytestream_len);
+            return TRUE;
+        }
+        case GST_QUERY_SCHEDULING:
+        {
+            gst_query_set_scheduling(query, GST_SCHEDULING_FLAG_SEEKABLE, 1, -1, 0);
+            gst_query_add_scheduling_mode(query, GST_PAD_MODE_PULL);
+            return TRUE;
+        }
+        case GST_QUERY_CAPS:
+        {
+            GstCaps *caps, *filter;
+
+            gst_query_parse_caps(query, &filter);
+
+            caps = gst_static_caps_get(&source_descs[source->type].bytestream_caps);
+
+            if (filter) {
+                GstCaps* filtered;
+                filtered = gst_caps_intersect_full(
+                        filter, caps, GST_CAPS_INTERSECT_FIRST);
+                gst_caps_unref(caps);
+                caps = filtered;
+            }
+            gst_query_set_caps_result(query, caps);
+            gst_caps_unref(caps);
+            return TRUE;
+        }
+        default:
+        {
+            WARN("Unhandled query type %s\n", GST_QUERY_TYPE_NAME(query));
+            return FALSE;
+        }
+    }
+}
+
+static gboolean activate_bytestream_pad_mode(GstPad *pad, GstObject *parent, GstPadMode mode, gboolean activate)
+{
+    struct media_source *source = gst_pad_get_element_private(pad);
+
+    TRACE("%s source pad for mediasource %p in %s mode.\n",
+            activate ? "Activating" : "Deactivating", source, gst_pad_mode_get_name(mode));
+
+    switch (mode) {
+      case GST_PAD_MODE_PULL:
+        return TRUE;
+      default:
+        return FALSE;
+    }
+    return FALSE;
+}
+
+static gboolean process_bytestream_pad_event(GstPad *pad, GstObject *parent, GstEvent *event)
+{
+    struct media_source *source = gst_pad_get_element_private(pad);
+
+    TRACE("source %p, type \"%s\".\n", source, GST_EVENT_TYPE_NAME(event));
+
+    switch (event->type) {
+        /* the seek event should fail in pull mode */
+        case GST_EVENT_SEEK:
+            return FALSE;
+        default:
+            WARN("Ignoring \"%s\" event.\n", GST_EVENT_TYPE_NAME(event));
+        case GST_EVENT_TAG:
+        case GST_EVENT_QOS:
+        case GST_EVENT_RECONFIGURE:
+            return gst_pad_event_default(pad, parent, event);
+    }
+    return TRUE;
+}
+
 static HRESULT WINAPI media_source_QueryInterface(IMFMediaSource *iface, REFIID riid, void **out)
 {
     struct media_source *source = impl_from_IMFMediaSource(iface);
@@ -187,13 +354,20 @@ static HRESULT WINAPI media_source_Pause(IMFMediaSource *iface)
 
     FIXME("(%p): stub\n", source);
 
+    if (source->state == SOURCE_SHUTDOWN)
+        return MF_E_SHUTDOWN;
+
     return E_NOTIMPL;
 }
 
 static HRESULT media_source_teardown(struct media_source *source)
 {
+    if (source->my_src)
+        gst_object_unref(GST_OBJECT(source->my_src));
     if (source->event_queue)
         IMFMediaEventQueue_Release(source->event_queue);
+    if (source->byte_stream)
+        IMFByteStream_Release(source->byte_stream);
 
     return S_OK;
 }
@@ -227,19 +401,35 @@ static const IMFMediaSourceVtbl IMFMediaSource_vtbl =
 
 static HRESULT media_source_constructor(IMFByteStream *bytestream, enum source_type type, struct media_source **out_media_source)
 {
+    GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE(
+        "mf_src",
+        GST_PAD_SRC,
+        GST_PAD_ALWAYS,
+        source_descs[type].bytestream_caps);
+
     struct media_source *object = heap_alloc_zero(sizeof(*object));
     HRESULT hr;
 
     if (!object)
         return E_OUTOFMEMORY;
 
+    object->IMFMediaSource_iface.lpVtbl = &IMFMediaSource_vtbl;
+    object->ref = 1;
+    object->type = type;
+    object->byte_stream = bytestream;
+    IMFByteStream_AddRef(bytestream);
+
     if (FAILED(hr = MFCreateEventQueue(&object->event_queue)))
         goto fail;
 
-    object->state = SOURCE_STOPPED;
+    object->my_src = gst_pad_new_from_static_template(&src_template, "mf-src");
+    gst_pad_set_element_private(object->my_src, object);
+    gst_pad_set_getrange_function(object->my_src, pull_from_bytestream_wrapper);
+    gst_pad_set_query_function(object->my_src, query_bytestream_wrapper);
+    gst_pad_set_activatemode_function(object->my_src, activate_bytestream_pad_mode_wrapper);
+    gst_pad_set_event_function(object->my_src, process_bytestream_pad_event_wrapper);
 
-    object->IMFMediaSource_iface.lpVtbl = &IMFMediaSource_vtbl;
-    object->ref = 1;
+    object->state = SOURCE_STOPPED;
 
     *out_media_source = object;
     return S_OK;
@@ -249,6 +439,7 @@ static HRESULT media_source_constructor(IMFByteStream *bytestream, enum source_t
 
     media_source_teardown(object);
     heap_free(object);
+    *out_media_source = NULL;
     return hr;
 }
 
@@ -712,4 +903,42 @@ HRESULT container_stream_handler_construct(REFIID riid, void **obj, enum source_
     IMFByteStreamHandler_Release(&this->IMFByteStreamHandler_iface);
 
     return hr;
-}
\ No newline at end of file
+}
+
+/* helper for callback forwarding */
+void perform_cb_media_source(struct cb_data *cbdata)
+{
+    switch(cbdata->type)
+    {
+    case PULL_FROM_BYTESTREAM:
+        {
+            struct getrange_data *data = &cbdata->u.getrange_data;
+            cbdata->u.getrange_data.ret = pull_from_bytestream(data->pad, data->parent,
+                    data->ofs, data->len, data->buf);
+            break;
+        }
+    case QUERY_BYTESTREAM:
+        {
+            struct query_function_data *data = &cbdata->u.query_function_data;
+            cbdata->u.query_function_data.ret = query_bytestream(data->pad, data->parent, data->query);
+            break;
+        }
+    case ACTIVATE_BYTESTREAM_PAD_MODE:
+        {
+            struct activate_mode_data *data = &cbdata->u.activate_mode_data;
+            cbdata->u.activate_mode_data.ret = activate_bytestream_pad_mode(data->pad, data->parent, data->mode, data->activate);
+            break;
+        }
+    case PROCESS_BYTESTREAM_PAD_EVENT:
+        {
+            struct event_src_data *data = &cbdata->u.event_src_data;
+            cbdata->u.event_src_data.ret = process_bytestream_pad_event(data->pad, data->parent, data->event);
+            break;
+        }
+    default:
+        {
+            ERR("Wrong callback forwarder called\n");
+            return;
+        }
+    }
+}
-- 
2.25.3

From 784ddbe27a44282d12c360e08a06d3847f4fd1b8 Mon Sep 17 00:00:00 2001
From: Derek Lesho <dlesho@codeweavers.com>
Date: Tue, 31 Mar 2020 13:34:57 -0500
Subject: [PATCH 08/46] winegstreamer: Find an appropriate demuxer for the
 source.

We can add a path later which uses decodebin as the demuxer, for use with formats not supported on windows.  (like Theora)

Signed-off-by: Derek Lesho <dlesho@codeweavers.com>
---
 dlls/winegstreamer/gst_cbs.c      | 15 ++++-
 dlls/winegstreamer/gst_cbs.h      |  2 +
 dlls/winegstreamer/media_source.c | 99 ++++++++++++++++++++++++++++++-
 3 files changed, 114 insertions(+), 2 deletions(-)

diff --git a/dlls/winegstreamer/gst_cbs.c b/dlls/winegstreamer/gst_cbs.c
index 141ab3c611..51b017ded6 100644
--- a/dlls/winegstreamer/gst_cbs.c
+++ b/dlls/winegstreamer/gst_cbs.c
@@ -358,4 +358,17 @@ gboolean process_bytestream_pad_event_wrapper(GstPad *pad, GstObject *parent, Gs
     call_cb(&cbdata);
 
     return cbdata.u.event_src_data.ret;
-}
\ No newline at end of file
+}
+
+GstBusSyncReply watch_source_bus_wrapper(GstBus *bus, GstMessage *message, gpointer user)
+{
+    struct cb_data cbdata = { WATCH_SOURCE_BUS };
+
+    cbdata.u.watch_bus_data.bus = bus;
+    cbdata.u.watch_bus_data.msg = message;
+    cbdata.u.watch_bus_data.user = user;
+
+    call_cb(&cbdata);
+
+    return cbdata.u.watch_bus_data.ret;
+}
diff --git a/dlls/winegstreamer/gst_cbs.h b/dlls/winegstreamer/gst_cbs.h
index 10e999feea..0d7acaf0b8 100644
--- a/dlls/winegstreamer/gst_cbs.h
+++ b/dlls/winegstreamer/gst_cbs.h
@@ -48,6 +48,7 @@ enum CB_TYPE {
     QUERY_BYTESTREAM,
     ACTIVATE_BYTESTREAM_PAD_MODE,
     PROCESS_BYTESTREAM_PAD_EVENT,
+    WATCH_SOURCE_BUS,
     MEDIA_SOURCE_MAX,
 };
 
@@ -164,5 +165,6 @@ GstFlowReturn pull_from_bytestream_wrapper(GstPad *pad, GstObject *parent, guint
 gboolean query_bytestream_wrapper(GstPad *pad, GstObject *parent, GstQuery *query) DECLSPEC_HIDDEN;
 gboolean activate_bytestream_pad_mode_wrapper(GstPad *pad, GstObject *parent, GstPadMode mode, gboolean activate) DECLSPEC_HIDDEN;
 gboolean process_bytestream_pad_event_wrapper(GstPad *pad, GstObject *parent, GstEvent *event) DECLSPEC_HIDDEN;
+GstBusSyncReply watch_source_bus_wrapper(GstBus *bus, GstMessage *message, gpointer user) DECLSPEC_HIDDEN;
 
 #endif
diff --git a/dlls/winegstreamer/media_source.c b/dlls/winegstreamer/media_source.c
index 48119e4a89..e1f5c77375 100644
--- a/dlls/winegstreamer/media_source.c
+++ b/dlls/winegstreamer/media_source.c
@@ -38,7 +38,12 @@ struct media_source
     enum source_type type;
     IMFMediaEventQueue *event_queue;
     IMFByteStream *byte_stream;
-    GstPad *my_src;
+    struct media_stream **streams;
+    ULONG stream_count;
+    GstBus *bus;
+    GstElement *container;
+    GstElement *demuxer;
+    GstPad *my_src, *their_sink;
     enum
     {
         SOURCE_OPENING,
@@ -202,6 +207,37 @@ static gboolean process_bytestream_pad_event(GstPad *pad, GstObject *parent, Gst
     return TRUE;
 }
 
+GstBusSyncReply watch_source_bus(GstBus *bus, GstMessage *message, gpointer user)
+{
+    struct media_source *source = (struct media_source *) user;
+    gchar *dbg_info = NULL;
+    GError *err = NULL;
+
+    TRACE("source %p message type %s\n", source, GST_MESSAGE_TYPE_NAME(message));
+
+    switch (message->type)
+    {
+        case GST_MESSAGE_ERROR:
+            gst_message_parse_error(message, &err, &dbg_info);
+            ERR("%s: %s\n", GST_OBJECT_NAME(message->src), err->message);
+            ERR("%s\n", dbg_info);
+            g_error_free(err);
+            g_free(dbg_info);
+            break;
+        case GST_MESSAGE_WARNING:
+            gst_message_parse_warning(message, &err, &dbg_info);
+            WARN("%s: %s\n", GST_OBJECT_NAME(message->src), err->message);
+            WARN("%s\n", dbg_info);
+            g_error_free(err);
+            g_free(dbg_info);
+            break;
+        default:
+            break;
+    }
+
+    return GST_BUS_PASS;
+}
+
 static HRESULT WINAPI media_source_QueryInterface(IMFMediaSource *iface, REFIID riid, void **out)
 {
     struct media_source *source = impl_from_IMFMediaSource(iface);
@@ -364,6 +400,13 @@ static HRESULT media_source_teardown(struct media_source *source)
 {
     if (source->my_src)
         gst_object_unref(GST_OBJECT(source->my_src));
+    if (source->their_sink)
+        gst_object_unref(GST_OBJECT(source->their_sink));
+    if (source->container)
+    {
+        gst_element_set_state(source->container, GST_STATE_NULL);
+        gst_object_unref(GST_OBJECT(source->container));
+    }
     if (source->event_queue)
         IMFMediaEventQueue_Release(source->event_queue);
     if (source->byte_stream)
@@ -408,7 +451,10 @@ static HRESULT media_source_constructor(IMFByteStream *bytestream, enum source_t
         source_descs[type].bytestream_caps);
 
     struct media_source *object = heap_alloc_zero(sizeof(*object));
+    GList *demuxer_list_one, *demuxer_list_two;
+    GstElementFactory *demuxer_factory = NULL;
     HRESULT hr;
+    int ret;
 
     if (!object)
         return E_OUTOFMEMORY;
@@ -422,6 +468,11 @@ static HRESULT media_source_constructor(IMFByteStream *bytestream, enum source_t
     if (FAILED(hr = MFCreateEventQueue(&object->event_queue)))
         goto fail;
 
+    object->container = gst_bin_new(NULL);
+    object->bus = gst_bus_new();
+    gst_bus_set_sync_handler(object->bus, watch_source_bus_wrapper, object, NULL);
+    gst_element_set_bus(object->container, object->bus);
+
     object->my_src = gst_pad_new_from_static_template(&src_template, "mf-src");
     gst_pad_set_element_private(object->my_src, object);
     gst_pad_set_getrange_function(object->my_src, pull_from_bytestream_wrapper);
@@ -429,6 +480,44 @@ static HRESULT media_source_constructor(IMFByteStream *bytestream, enum source_t
     gst_pad_set_activatemode_function(object->my_src, activate_bytestream_pad_mode_wrapper);
     gst_pad_set_event_function(object->my_src, process_bytestream_pad_event_wrapper);
 
+    /* Find demuxer */
+    demuxer_list_one = gst_element_factory_list_get_elements(GST_ELEMENT_FACTORY_TYPE_DEMUXER, 1);
+
+    demuxer_list_two = gst_element_factory_list_filter(demuxer_list_one, gst_static_caps_get(&source_descs[type].bytestream_caps), GST_PAD_SINK, 0);
+    gst_plugin_feature_list_free(demuxer_list_one);
+
+    if (!(g_list_length(demuxer_list_two)))
+    {
+        ERR("Failed to find demuxer for source.\n");
+        gst_plugin_feature_list_free(demuxer_list_two);
+        hr = E_FAIL;
+        goto fail;
+    }
+
+    demuxer_factory = g_list_first(demuxer_list_two)->data;
+    gst_object_ref(demuxer_factory);
+    gst_plugin_feature_list_free(demuxer_list_two);
+
+    TRACE("Found demuxer %s.\n", GST_ELEMENT_NAME(demuxer_factory));
+
+    object->demuxer = gst_element_factory_create(demuxer_factory, NULL);
+    if (!(object->demuxer))
+    {
+        WARN("Failed to create demuxer for source\n");
+        hr = E_OUTOFMEMORY;
+        goto fail;
+    }
+    gst_bin_add(GST_BIN(object->container), object->demuxer);
+
+    object->their_sink = gst_element_get_static_pad(object->demuxer, "sink");
+
+    if ((ret = gst_pad_link(object->my_src, object->their_sink)) < 0)
+    {
+        WARN("Failed to link our bytestream pad to the demuxer input\n");
+        hr = E_OUTOFMEMORY;
+        goto fail;
+    }
+
     object->state = SOURCE_STOPPED;
 
     *out_media_source = object;
@@ -437,6 +526,8 @@ static HRESULT media_source_constructor(IMFByteStream *bytestream, enum source_t
     fail:
     WARN("Failed to construct MFMediaSource, hr %#x.\n", hr);
 
+    if (demuxer_factory)
+        gst_object_unref(demuxer_factory);
     media_source_teardown(object);
     heap_free(object);
     *out_media_source = NULL;
@@ -935,6 +1026,12 @@ void perform_cb_media_source(struct cb_data *cbdata)
             cbdata->u.event_src_data.ret = process_bytestream_pad_event(data->pad, data->parent, data->event);
             break;
         }
+    case WATCH_SOURCE_BUS:
+        {
+            struct watch_bus_data *data = &cbdata->u.watch_bus_data;
+            cbdata->u.watch_bus_data.ret = watch_source_bus(data->bus, data->msg, data->user);
+            break;
+        }
     default:
         {
             ERR("Wrong callback forwarder called\n");
-- 
2.25.3

From adea62e016012e704038bf50c177796065d3a24a Mon Sep 17 00:00:00 2001
From: Derek Lesho <dlesho@codeweavers.com>
Date: Mon, 20 Apr 2020 13:02:23 -0500
Subject: [PATCH 09/46] winegstreamer: Use the demuxer to establish
 IMFMediaStreams.

Signed-off-by: Derek Lesho <dlesho@codeweavers.com>
---
 dlls/winegstreamer/gst_cbs.c      |  44 ++++
 dlls/winegstreamer/gst_cbs.h      |  13 +
 dlls/winegstreamer/media_source.c | 404 ++++++++++++++++++++++++++++++
 3 files changed, 461 insertions(+)

diff --git a/dlls/winegstreamer/gst_cbs.c b/dlls/winegstreamer/gst_cbs.c
index 51b017ded6..808c9f7ad2 100644
--- a/dlls/winegstreamer/gst_cbs.c
+++ b/dlls/winegstreamer/gst_cbs.c
@@ -372,3 +372,47 @@ GstBusSyncReply watch_source_bus_wrapper(GstBus *bus, GstMessage *message, gpoin
 
     return cbdata.u.watch_bus_data.ret;
 }
+
+void source_stream_added_wrapper(GstElement *bin, GstPad *pad, gpointer user)
+{
+    struct cb_data cbdata = { SOURCE_STREAM_ADDED };
+
+    cbdata.u.pad_added_data.element = bin;
+    cbdata.u.pad_added_data.pad = pad;
+    cbdata.u.pad_added_data.user = user;
+
+    call_cb(&cbdata);
+}
+
+void source_stream_removed_wrapper(GstElement *element, GstPad *pad, gpointer user)
+{
+    struct cb_data cbdata = { SOURCE_STREAM_REMOVED };
+
+    cbdata.u.pad_removed_data.element = element;
+    cbdata.u.pad_removed_data.pad = pad;
+    cbdata.u.pad_removed_data.user = user;
+
+    call_cb(&cbdata);
+}
+
+void source_all_streams_wrapper(GstElement *element, gpointer user)
+{
+    struct cb_data cbdata = { SOURCE_ALL_STREAMS };
+
+    cbdata.u.no_more_pads_data.element = element;
+    cbdata.u.no_more_pads_data.user = user;
+
+    call_cb(&cbdata);
+}
+
+GstFlowReturn stream_new_sample_wrapper(GstElement *appsink, gpointer user)
+{
+    struct cb_data cbdata = { STREAM_NEW_SAMPLE };
+
+    cbdata.u.new_sample_data.appsink = appsink;
+    cbdata.u.new_sample_data.user = user;
+
+    call_cb(&cbdata);
+
+    return cbdata.u.new_sample_data.ret;
+}
\ No newline at end of file
diff --git a/dlls/winegstreamer/gst_cbs.h b/dlls/winegstreamer/gst_cbs.h
index 0d7acaf0b8..106368a064 100644
--- a/dlls/winegstreamer/gst_cbs.h
+++ b/dlls/winegstreamer/gst_cbs.h
@@ -49,6 +49,10 @@ enum CB_TYPE {
     ACTIVATE_BYTESTREAM_PAD_MODE,
     PROCESS_BYTESTREAM_PAD_EVENT,
     WATCH_SOURCE_BUS,
+    SOURCE_STREAM_ADDED,
+    SOURCE_STREAM_REMOVED,
+    SOURCE_ALL_STREAMS,
+    STREAM_NEW_SAMPLE,
     MEDIA_SOURCE_MAX,
 };
 
@@ -134,6 +138,11 @@ struct cb_data {
             GstQuery *query;
             gboolean ret;
         } query_sink_data;
+        struct new_sample_data {
+            GstElement *appsink;
+            gpointer user;
+            GstFlowReturn ret;
+        } new_sample_data;
     } u;
 
     int finished;
@@ -166,5 +175,9 @@ gboolean query_bytestream_wrapper(GstPad *pad, GstObject *parent, GstQuery *quer
 gboolean activate_bytestream_pad_mode_wrapper(GstPad *pad, GstObject *parent, GstPadMode mode, gboolean activate) DECLSPEC_HIDDEN;
 gboolean process_bytestream_pad_event_wrapper(GstPad *pad, GstObject *parent, GstEvent *event) DECLSPEC_HIDDEN;
 GstBusSyncReply watch_source_bus_wrapper(GstBus *bus, GstMessage *message, gpointer user) DECLSPEC_HIDDEN;
+void source_stream_added_wrapper(GstElement *bin, GstPad *pad, gpointer user) DECLSPEC_HIDDEN;
+void source_stream_removed_wrapper(GstElement *element, GstPad *pad, gpointer user) DECLSPEC_HIDDEN;
+void source_all_streams_wrapper(GstElement *element, gpointer user) DECLSPEC_HIDDEN;
+GstFlowReturn stream_new_sample_wrapper(GstElement *appsink, gpointer user) DECLSPEC_HIDDEN;
 
 #endif
diff --git a/dlls/winegstreamer/media_source.c b/dlls/winegstreamer/media_source.c
index e1f5c77375..8335c64338 100644
--- a/dlls/winegstreamer/media_source.c
+++ b/dlls/winegstreamer/media_source.c
@@ -5,6 +5,7 @@
 #include "gst_private.h"
 #include "gst_cbs.h"
 
+#include <assert.h>
 #include <stdarg.h>
 
 #define COBJMACROS
@@ -31,6 +32,26 @@ static struct source_desc
     }
 };
 
+struct media_stream
+{
+    IMFMediaStream IMFMediaStream_iface;
+    LONG ref;
+    struct media_source *parent_source;
+    IMFMediaEventQueue *event_queue;
+    IMFStreamDescriptor *descriptor;
+    GstElement *appsink;
+    GstPad *their_src, *my_sink;
+    /* usually reflects state of source */
+    enum
+    {
+        STREAM_INACTIVE,
+        STREAM_ENABLED,
+        STREAM_PAUSED,
+        STREAM_RUNNING,
+        STREAM_SHUTDOWN,
+    } state;
+};
+
 struct media_source
 {
     IMFMediaSource IMFMediaSource_iface;
@@ -52,13 +73,32 @@ struct media_source
         SOURCE_RUNNING,
         SOURCE_SHUTDOWN,
     } state;
+    HANDLE all_streams_event;
 };
 
+static inline struct media_stream *impl_from_IMFMediaStream(IMFMediaStream *iface)
+{
+    return CONTAINING_RECORD(iface, struct media_stream, IMFMediaStream_iface);
+}
+
 static inline struct media_source *impl_from_IMFMediaSource(IMFMediaSource *iface)
 {
     return CONTAINING_RECORD(iface, struct media_source, IMFMediaSource_iface);
 }
 
+/* inactive stream sample discarder */
+static GstFlowReturn stream_new_sample(GstElement *appsink, gpointer user)
+{
+    struct media_stream *stream = (struct media_stream *) user;
+    GstSample *discard_sample;
+
+    TRACE("(%p) got sample\n", stream);
+
+    g_signal_emit_by_name(stream->appsink, "pull-sample", &discard_sample);
+    gst_sample_unref(discard_sample);
+    return GST_FLOW_OK;
+}
+
 GstFlowReturn pull_from_bytestream(GstPad *pad, GstObject *parent, guint64 ofs, guint len,
         GstBuffer **buf)
 {
@@ -238,6 +278,314 @@ GstBusSyncReply watch_source_bus(GstBus *bus, GstMessage *message, gpointer user
     return GST_BUS_PASS;
 }
 
+static HRESULT media_stream_constructor(struct media_source *source, GstPad *pad, DWORD stream_id, struct media_stream **out_stream);
+static void source_stream_added(GstElement *element, GstPad *pad, gpointer user)
+{
+    struct media_source *source = (struct media_source *) user;
+    struct media_stream **new_stream_array;
+    struct media_stream *stream;
+    gchar *g_stream_id;
+    DWORD stream_id;
+
+    /* Most/All seen randomly calculate the initial part of the stream id, the last three digits are the only deterministic part */
+    g_stream_id = gst_pad_get_stream_id(pad);
+    sscanf(strstr(g_stream_id, "/"), "/%03u", &stream_id);
+    g_free(g_stream_id);
+
+    TRACE("stream-id: %u\n", stream_id);
+
+    /* find existing stream */
+    for (unsigned int i = 0; i < source->stream_count; i++)
+    {
+        DWORD existing_stream_id;
+        IMFStreamDescriptor *descriptor = source->streams[i]->descriptor;
+
+        if (FAILED(IMFStreamDescriptor_GetStreamIdentifier(descriptor, &existing_stream_id)))
+            goto leave;
+
+        if (existing_stream_id == stream_id)
+        {
+            struct media_stream *existing_stream = source->streams[i];
+            GstPadLinkReturn ret;
+
+            TRACE("Found existing stream %p\n", existing_stream);
+
+            if (!existing_stream->my_sink)
+            {
+                ERR("Couldn't find our sink\n");
+                goto leave;
+            }
+
+            existing_stream->their_src = pad;
+            gst_pad_set_element_private(pad, existing_stream);
+
+            if ((ret = gst_pad_link(existing_stream->their_src, existing_stream->my_sink)) != GST_PAD_LINK_OK)
+            {
+                ERR("Error linking demuxer to stream %p, err = %d\n", existing_stream, ret);
+            }
+            gst_element_sync_state_with_parent(existing_stream->appsink);
+
+            goto leave;
+        }
+    }
+
+    if (FAILED(media_stream_constructor(source, pad, stream_id, &stream)))
+    {
+        goto leave;
+    }
+
+    if (!(new_stream_array = heap_realloc(source->streams, (source->stream_count + 1) * (sizeof(*new_stream_array)))))
+    {
+        ERR("Failed to add stream to source\n");
+        goto leave;
+    }
+
+    source->streams = new_stream_array;
+    source->streams[source->stream_count++] = stream;
+
+    leave:
+    return;
+}
+
+static void source_stream_removed(GstElement *element, GstPad *pad, gpointer user)
+{
+    struct media_stream *stream;
+
+    if (gst_pad_get_direction(pad) != GST_PAD_SRC)
+        return;
+
+    stream = (struct media_stream *) gst_pad_get_element_private(pad);
+
+    if (stream)
+    {
+        TRACE("Stream %p of Source %p removed\n", stream, stream->parent_source);
+
+        assert (stream->their_src == pad);
+
+        gst_pad_unlink(stream->their_src, stream->my_sink);
+
+        stream->their_src = NULL;
+        gst_pad_set_element_private(pad, NULL);
+    }
+}
+
+static void source_all_streams(GstElement *element, gpointer user)
+{
+    struct media_source *source = (struct media_source *) user;
+
+    SetEvent(source->all_streams_event);
+}
+
+static HRESULT WINAPI media_stream_QueryInterface(IMFMediaStream *iface, REFIID riid, void **out)
+{
+    struct media_stream *stream = impl_from_IMFMediaStream(iface);
+
+    TRACE("(%p)->(%s %p)\n", stream, debugstr_guid(riid), out);
+
+    if (IsEqualIID(riid, &IID_IMFMediaStream) ||
+        IsEqualIID(riid, &IID_IMFMediaEventGenerator) ||
+        IsEqualIID(riid, &IID_IUnknown))
+    {
+        *out = &stream->IMFMediaStream_iface;
+    }
+    else
+    {
+        FIXME("(%s, %p)\n", debugstr_guid(riid), out);
+        *out = NULL;
+        return E_NOINTERFACE;
+    }
+
+    IUnknown_AddRef((IUnknown*)*out);
+    return S_OK;
+}
+
+static ULONG WINAPI media_stream_AddRef(IMFMediaStream *iface)
+{
+    struct media_stream *stream = impl_from_IMFMediaStream(iface);
+    ULONG ref = InterlockedIncrement(&stream->ref);
+
+    TRACE("(%p) ref=%u\n", stream, ref);
+
+    return ref;
+}
+
+static ULONG WINAPI media_stream_Release(IMFMediaStream *iface)
+{
+    struct media_stream *stream = impl_from_IMFMediaStream(iface);
+
+    ULONG ref = InterlockedDecrement(&stream->ref);
+
+    TRACE("(%p) ref=%u\n", stream, ref);
+
+    if (!ref)
+    {
+        if (stream->their_src)
+            gst_object_unref(GST_OBJECT(stream->their_src));
+        if (stream->my_sink)
+            gst_object_unref(GST_OBJECT(stream->my_sink));
+
+        if (stream->descriptor)
+            IMFStreamDescriptor_Release(stream->descriptor);
+        if (stream->event_queue)
+            IMFMediaEventQueue_Release(stream->event_queue);
+        if (stream->parent_source)
+            IMFMediaSource_Release(&stream->parent_source->IMFMediaSource_iface);
+
+        heap_free(stream);
+    }
+
+    return ref;
+}
+
+static HRESULT WINAPI media_stream_GetEvent(IMFMediaStream *iface, DWORD flags, IMFMediaEvent **event)
+{
+    struct media_stream *stream = impl_from_IMFMediaStream(iface);
+
+    TRACE("(%p)->(%#x, %p)\n", stream, flags, event);
+
+    if (stream->state == STREAM_SHUTDOWN)
+        return MF_E_SHUTDOWN;
+
+    return IMFMediaEventQueue_GetEvent(stream->event_queue, flags, event);
+}
+
+static HRESULT WINAPI media_stream_BeginGetEvent(IMFMediaStream *iface, IMFAsyncCallback *callback, IUnknown *state)
+{
+    struct media_stream *stream = impl_from_IMFMediaStream(iface);
+
+    TRACE("(%p)->(%p, %p)\n", stream, callback, state);
+
+    if (stream->state == STREAM_SHUTDOWN)
+        return MF_E_SHUTDOWN;
+
+    return IMFMediaEventQueue_BeginGetEvent(stream->event_queue, callback, state);
+}
+
+static HRESULT WINAPI media_stream_EndGetEvent(IMFMediaStream *iface, IMFAsyncResult *result, IMFMediaEvent **event)
+{
+    struct media_stream *stream = impl_from_IMFMediaStream(iface);
+
+    TRACE("(%p)->(%p, %p)\n", stream, result, event);
+
+    if (stream->state == STREAM_SHUTDOWN)
+        return MF_E_SHUTDOWN;
+
+    return IMFMediaEventQueue_EndGetEvent(stream->event_queue, result, event);
+}
+
+static HRESULT WINAPI media_stream_QueueEvent(IMFMediaStream *iface, MediaEventType event_type, REFGUID ext_type,
+        HRESULT hr, const PROPVARIANT *value)
+{
+    struct media_stream *stream = impl_from_IMFMediaStream(iface);
+
+    TRACE("(%p)->(%d, %s, %#x, %p)\n", stream, event_type, debugstr_guid(ext_type), hr, value);
+
+    if (stream->state == STREAM_SHUTDOWN)
+        return MF_E_SHUTDOWN;
+
+    return IMFMediaEventQueue_QueueEventParamVar(stream->event_queue, event_type, ext_type, hr, value);
+}
+
+static HRESULT WINAPI media_stream_GetMediaSource(IMFMediaStream *iface, IMFMediaSource **source)
+{
+    struct media_stream *stream = impl_from_IMFMediaStream(iface);
+
+    FIXME("stub (%p)->(%p)\n", stream, source);
+
+    if (stream->state == STREAM_SHUTDOWN)
+        return MF_E_SHUTDOWN;
+
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI media_stream_GetStreamDescriptor(IMFMediaStream* iface, IMFStreamDescriptor **descriptor)
+{
+    struct media_stream *stream = impl_from_IMFMediaStream(iface);
+
+    TRACE("(%p)->(%p)\n", stream, descriptor);
+
+    if (stream->state == STREAM_SHUTDOWN)
+        return MF_E_SHUTDOWN;
+
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI media_stream_RequestSample(IMFMediaStream *iface, IUnknown *token)
+{
+    struct media_stream *stream = impl_from_IMFMediaStream(iface);
+
+    TRACE("(%p)->(%p)\n", iface, token);
+
+    if (stream->state == STREAM_SHUTDOWN)
+        return MF_E_SHUTDOWN;
+
+    return E_NOTIMPL;
+}
+
+static const IMFMediaStreamVtbl media_stream_vtbl =
+{
+    media_stream_QueryInterface,
+    media_stream_AddRef,
+    media_stream_Release,
+    media_stream_GetEvent,
+    media_stream_BeginGetEvent,
+    media_stream_EndGetEvent,
+    media_stream_QueueEvent,
+    media_stream_GetMediaSource,
+    media_stream_GetStreamDescriptor,
+    media_stream_RequestSample
+};
+
+static HRESULT media_stream_constructor(struct media_source *source, GstPad *pad, DWORD stream_id, struct media_stream **out_stream)
+{
+    struct media_stream *object = heap_alloc_zero(sizeof(*object));
+    HRESULT hr;
+
+    TRACE("(%p %p)->(%p)\n", source, pad, out_stream);
+
+    object->IMFMediaStream_iface.lpVtbl = &media_stream_vtbl;
+    object->ref = 1;
+
+    IMFMediaSource_AddRef(&source->IMFMediaSource_iface);
+    object->parent_source = source;
+    object->their_src = pad;
+
+    object->state = STREAM_INACTIVE;
+
+    if (FAILED(hr = MFCreateEventQueue(&object->event_queue)))
+        goto fail;
+
+    if (!(object->appsink = gst_element_factory_make("appsink", NULL)))
+    {
+        hr = E_OUTOFMEMORY;
+        goto fail;
+    }
+    gst_bin_add(GST_BIN(object->parent_source->container), object->appsink);
+
+    g_object_set(object->appsink, "emit-signals", TRUE, NULL);
+    g_object_set(object->appsink, "sync", FALSE, NULL);
+    g_object_set(object->appsink, "async", FALSE, NULL); /* <- This allows us interact with the bin w/o prerolling */
+    g_signal_connect(object->appsink, "new-sample", G_CALLBACK(stream_new_sample_wrapper), object);
+
+    object->my_sink = gst_element_get_static_pad(object->appsink, "sink");
+    gst_pad_set_element_private(pad, object);
+
+    gst_pad_link(object->their_src, object->my_sink);
+
+    gst_element_sync_state_with_parent(object->appsink);
+
+    TRACE("->(%p)\n", object);
+
+    *out_stream = object;
+    return S_OK;
+
+    fail:
+    WARN("Failed to construct media stream, hr %#x.\n", hr);
+
+    IMFMediaStream_Release(&object->IMFMediaStream_iface);
+    return hr;
+}
+
 static HRESULT WINAPI media_source_QueryInterface(IMFMediaSource *iface, REFIID riid, void **out)
 {
     struct media_source *source = impl_from_IMFMediaSource(iface);
@@ -412,6 +760,18 @@ static HRESULT media_source_teardown(struct media_source *source)
     if (source->byte_stream)
         IMFByteStream_Release(source->byte_stream);
 
+    for (unsigned int i = 0; i < source->stream_count; i++)
+    {
+        source->streams[i]->state = STREAM_SHUTDOWN;
+        IMFMediaStream_Release(&source->streams[i]->IMFMediaStream_iface);
+    }
+
+    if (source->stream_count)
+        heap_free(source->streams);
+
+    if (source->all_streams_event)
+        CloseHandle(source->all_streams_event);
+
     return S_OK;
 }
 
@@ -464,6 +824,7 @@ static HRESULT media_source_constructor(IMFByteStream *bytestream, enum source_t
     object->type = type;
     object->byte_stream = bytestream;
     IMFByteStream_AddRef(bytestream);
+    object->all_streams_event = CreateEventA(NULL, FALSE, FALSE, NULL);
 
     if (FAILED(hr = MFCreateEventQueue(&object->event_queue)))
         goto fail;
@@ -509,6 +870,10 @@ static HRESULT media_source_constructor(IMFByteStream *bytestream, enum source_t
     }
     gst_bin_add(GST_BIN(object->container), object->demuxer);
 
+    g_signal_connect(object->demuxer, "pad-added", G_CALLBACK(source_stream_added_wrapper), object);
+    g_signal_connect(object->demuxer, "pad-removed", G_CALLBACK(source_stream_removed_wrapper), object);
+    g_signal_connect(object->demuxer, "no-more-pads", G_CALLBACK(source_all_streams_wrapper), object);
+
     object->their_sink = gst_element_get_static_pad(object->demuxer, "sink");
 
     if ((ret = gst_pad_link(object->my_src, object->their_sink)) < 0)
@@ -518,6 +883,21 @@ static HRESULT media_source_constructor(IMFByteStream *bytestream, enum source_t
         goto fail;
     }
 
+    object->state = SOURCE_OPENING;
+
+    gst_element_set_state(object->container, GST_STATE_PAUSED);
+    ret = gst_element_get_state(object->container, NULL, NULL, -1);
+    if (ret == GST_STATE_CHANGE_FAILURE)
+    {
+        ERR("Failed to play source.\n");
+        hr = E_OUTOFMEMORY;
+        goto fail;
+    }
+
+    WaitForSingleObject(object->all_streams_event, INFINITE);
+
+    gst_element_set_state(object->container, GST_STATE_READY);
+
     object->state = SOURCE_STOPPED;
 
     *out_media_source = object;
@@ -1032,6 +1412,30 @@ void perform_cb_media_source(struct cb_data *cbdata)
             cbdata->u.watch_bus_data.ret = watch_source_bus(data->bus, data->msg, data->user);
             break;
         }
+    case SOURCE_STREAM_ADDED:
+        {
+            struct pad_added_data *data = &cbdata->u.pad_added_data;
+            source_stream_added(data->element, data->pad, data->user);
+            break;
+        }
+    case SOURCE_STREAM_REMOVED:
+        {
+            struct pad_removed_data *data = &cbdata->u.pad_removed_data;
+            source_stream_removed(data->element, data->pad, data->user);
+            break;
+        }
+    case SOURCE_ALL_STREAMS:
+        {
+            struct no_more_pads_data *data = &cbdata->u.no_more_pads_data;
+            source_all_streams(data->element, data->user);
+            break;
+        }
+    case STREAM_NEW_SAMPLE:
+        {
+            struct new_sample_data *data = &cbdata->u.new_sample_data;
+            cbdata->u.new_sample_data.ret = stream_new_sample(data->appsink, data->user);
+            break;
+        }
     default:
         {
             ERR("Wrong callback forwarder called\n");
-- 
2.25.3

From 26e4bdb4f4b3565c70376e5b618cb5eb81a7a97a Mon Sep 17 00:00:00 2001
From: Derek Lesho <dlesho@codeweavers.com>
Date: Tue, 31 Mar 2020 11:11:05 -0500
Subject: [PATCH 10/46] winegstreamer: Implement
 IMFMediaStream::GetStreamDescriptor.

Signed-off-by: Derek Lesho <dlesho@codeweavers.com>
---
 dlls/winegstreamer/gst_private.h  |   4 +
 dlls/winegstreamer/media_source.c |  45 +++++++-
 dlls/winegstreamer/mfplat.c       | 186 ++++++++++++++++++++++++++++++
 3 files changed, 234 insertions(+), 1 deletion(-)

diff --git a/dlls/winegstreamer/gst_private.h b/dlls/winegstreamer/gst_private.h
index 71ca429088..780cf1b02f 100644
--- a/dlls/winegstreamer/gst_private.h
+++ b/dlls/winegstreamer/gst_private.h
@@ -36,6 +36,7 @@
 #include "winuser.h"
 #include "dshow.h"
 #include "strmif.h"
+#include "mfobjects.h"
 #include "wine/heap.h"
 #include "wine/strmbase.h"
 
@@ -54,6 +55,9 @@ void start_dispatch_thread(void) DECLSPEC_HIDDEN;
 
 extern HRESULT mfplat_get_class_object(REFCLSID rclsid, REFIID riid, void **obj) DECLSPEC_HIDDEN;
 
+GstCaps *make_mf_compatible_caps(GstCaps *caps);
+IMFMediaType *mf_media_type_from_caps(GstCaps *caps);
+
 enum source_type
 {
     SOURCE_TYPE_MPEG_4,
diff --git a/dlls/winegstreamer/media_source.c b/dlls/winegstreamer/media_source.c
index 8335c64338..a654d555bc 100644
--- a/dlls/winegstreamer/media_source.c
+++ b/dlls/winegstreamer/media_source.c
@@ -507,7 +507,10 @@ static HRESULT WINAPI media_stream_GetStreamDescriptor(IMFMediaStream* iface, IM
     if (stream->state == STREAM_SHUTDOWN)
         return MF_E_SHUTDOWN;
 
-    return E_NOTIMPL;
+    IMFStreamDescriptor_AddRef(stream->descriptor);
+    *descriptor = stream->descriptor;
+
+    return S_OK;
 }
 
 static HRESULT WINAPI media_stream_RequestSample(IMFMediaStream *iface, IUnknown *token)
@@ -539,6 +542,9 @@ static const IMFMediaStreamVtbl media_stream_vtbl =
 static HRESULT media_stream_constructor(struct media_source *source, GstPad *pad, DWORD stream_id, struct media_stream **out_stream)
 {
     struct media_stream *object = heap_alloc_zero(sizeof(*object));
+    IMFMediaTypeHandler *type_handler = NULL;
+    IMFMediaType *stream_type = NULL;
+    GstCaps *caps = NULL;
     HRESULT hr;
 
     TRACE("(%p %p)->(%p)\n", source, pad, out_stream);
@@ -567,6 +573,36 @@ static HRESULT media_stream_constructor(struct media_source *source, GstPad *pad
     g_object_set(object->appsink, "async", FALSE, NULL); /* <- This allows us interact with the bin w/o prerolling */
     g_signal_connect(object->appsink, "new-sample", G_CALLBACK(stream_new_sample_wrapper), object);
 
+    if (!(caps = gst_pad_query_caps(pad, NULL)))
+        goto fail;
+
+    if (TRACE_ON(mfplat))
+    {
+        gchar *caps_str = gst_caps_to_string(caps);
+        TRACE("caps %s\n", debugstr_a(caps_str));
+        g_free(caps_str);
+    }
+
+    if (!(stream_type = mf_media_type_from_caps(caps)))
+        goto fail;
+
+    gst_caps_unref(caps);
+    caps = NULL;
+
+    if (FAILED(hr = MFCreateStreamDescriptor(stream_id, 1, &stream_type, &object->descriptor)))
+        goto fail;
+
+    if (FAILED(hr = IMFStreamDescriptor_GetMediaTypeHandler(object->descriptor, &type_handler)))
+        goto fail;
+
+    if (FAILED(hr = IMFMediaTypeHandler_SetCurrentMediaType(type_handler, stream_type)))
+        goto fail;
+
+    IMFMediaTypeHandler_Release(type_handler);
+    type_handler = NULL;
+    IMFMediaType_Release(stream_type);
+    stream_type = NULL;
+
     object->my_sink = gst_element_get_static_pad(object->appsink, "sink");
     gst_pad_set_element_private(pad, object);
 
@@ -582,6 +618,13 @@ static HRESULT media_stream_constructor(struct media_source *source, GstPad *pad
     fail:
     WARN("Failed to construct media stream, hr %#x.\n", hr);
 
+    if (caps)
+        gst_caps_unref(caps);
+    if (stream_type)
+        IMFMediaType_Release(stream_type);
+    if (type_handler)
+        IMFMediaTypeHandler_Release(type_handler);
+
     IMFMediaStream_Release(&object->IMFMediaStream_iface);
     return hr;
 }
diff --git a/dlls/winegstreamer/mfplat.c b/dlls/winegstreamer/mfplat.c
index 16e55247de..09266ffab4 100644
--- a/dlls/winegstreamer/mfplat.c
+++ b/dlls/winegstreamer/mfplat.c
@@ -16,6 +16,11 @@
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
  */
 
+#include "config.h"
+#include <gst/gst.h>
+
+#include "gst_private.h"
+
 #include <stdarg.h>
 
 #include "gst_private.h"
@@ -442,3 +447,184 @@ HRESULT mfplat_get_class_object(REFCLSID rclsid, REFIID riid, void **obj)
 
     return CLASS_E_CLASSNOTAVAILABLE;
 }
+
+const static struct
+{
+    const GUID *subtype;
+    GstVideoFormat format;
+}
+uncompressed_formats[] =
+{
+    {&MFVideoFormat_ARGB32,  GST_VIDEO_FORMAT_BGRA},
+    {&MFVideoFormat_RGB32,   GST_VIDEO_FORMAT_BGRx},
+    {&MFVideoFormat_RGB24,   GST_VIDEO_FORMAT_BGR},
+    {&MFVideoFormat_RGB565,  GST_VIDEO_FORMAT_BGR16},
+    {&MFVideoFormat_RGB555,  GST_VIDEO_FORMAT_BGR15},
+};
+
+/* caps will be modified to represent the exact type needed for the format */
+static IMFMediaType* transform_to_media_type(GstCaps *caps)
+{
+    IMFMediaType *media_type;
+    GstStructure *info;
+    const char *mime_type;
+
+    if (TRACE_ON(mfplat))
+    {
+        gchar *human_readable = gst_caps_to_string(caps);
+        TRACE("caps = %s\n", debugstr_a(human_readable));
+        g_free(human_readable);
+    }
+
+    if (FAILED(MFCreateMediaType(&media_type)))
+    {
+        return NULL;
+    }
+
+    info = gst_caps_get_structure(caps, 0);
+    mime_type = gst_structure_get_name(info);
+
+    if (!(strncmp(mime_type, "video", 5)))
+    {
+        GstVideoInfo video_info;
+
+        if (!(gst_video_info_from_caps(&video_info, caps)))
+        {
+            return NULL;
+        }
+
+        IMFMediaType_SetGUID(media_type, &MF_MT_MAJOR_TYPE, &MFMediaType_Video);
+
+        IMFMediaType_SetUINT64(media_type, &MF_MT_FRAME_SIZE, ((UINT64)video_info.width << 32) | video_info.height);
+
+        IMFMediaType_SetUINT64(media_type, &MF_MT_FRAME_RATE, ((UINT64)video_info.fps_n << 32) | video_info.fps_d);
+
+        if (!(strcmp(mime_type, "video/x-raw")))
+        {
+            GUID fourcc_subtype = MFVideoFormat_Base;
+            unsigned int i;
+
+            IMFMediaType_SetUINT32(media_type, &MF_MT_COMPRESSED, FALSE);
+
+            /* First try FOURCC */
+            if ((fourcc_subtype.Data1 = gst_video_format_to_fourcc(video_info.finfo->format)))
+            {
+                IMFMediaType_SetGUID(media_type, &MF_MT_SUBTYPE, &fourcc_subtype);
+            }
+            else
+            {
+                for (i = 0; i < ARRAY_SIZE(uncompressed_formats); i++)
+                {
+                    if (uncompressed_formats[i].format == video_info.finfo->format)
+                    {
+                        IMFMediaType_SetGUID(media_type, &MF_MT_SUBTYPE, uncompressed_formats[i].subtype);
+                        break;
+                    }
+                }
+                if (i == ARRAY_SIZE(uncompressed_formats))
+                    FIXME("Unrecognized format.\n");
+            }
+        }
+        else
+            FIXME("Unrecognized video format %s\n", mime_type);
+    }
+    else if (!(strncmp(mime_type, "audio", 5)))
+    {
+        IMFMediaType_SetGUID(media_type, &MF_MT_MAJOR_TYPE, &MFMediaType_Audio);
+
+        if (!(strcmp(mime_type, "audio/x-raw")))
+        {
+            const char *format;
+            if ((format = gst_structure_get_string(info, "format")))
+            {
+                char type;
+                unsigned int bits_per_sample;
+                char endian[2];
+                char new_format[6];
+                if ((strlen(format) > 5) || (sscanf(format, "%c%u%2c", &type, &bits_per_sample, endian) < 2))
+                {
+                    FIXME("Unhandled audio format %s\n", format);
+                    IMFMediaType_Release(media_type);
+                    return NULL;
+                }
+
+                if (type == 'F')
+                {
+                    IMFMediaType_SetGUID(media_type, &MF_MT_SUBTYPE, &MFAudioFormat_Float);
+                }
+                else if (type == 'U' || type == 'S')
+                {
+                    IMFMediaType_SetGUID(media_type, &MF_MT_SUBTYPE, &MFAudioFormat_PCM);
+                    if (bits_per_sample == 8)
+                        type = 'U';
+                    else
+                        type = 'S';
+                }
+                else
+                {
+                    FIXME("Unrecognized audio format: %s\n", format);
+                    IMFMediaType_Release(media_type);
+                    return NULL;
+                }
+
+                IMFMediaType_SetUINT32(media_type, &MF_MT_AUDIO_BITS_PER_SAMPLE, bits_per_sample);
+
+                if (endian[0] == 'B')
+                    endian[0] = 'L';
+
+                sprintf(new_format, "%c%u%.2s", type, bits_per_sample, bits_per_sample > 8 ? endian : 0);
+                gst_caps_set_simple(caps, "format", G_TYPE_STRING, new_format, NULL);
+            }
+            else
+            {
+                ERR("Failed to get audio format\n");
+            }
+        }
+        else
+            FIXME("Unrecognized audio format %s\n", mime_type);
+    }
+    else
+    {
+        IMFMediaType_Release(media_type);
+        return NULL;
+    }
+
+    return media_type;
+}
+
+/* returns NULL if doesn't match exactly */
+IMFMediaType *mf_media_type_from_caps(GstCaps *caps)
+{
+    GstCaps *writeable_caps;
+    IMFMediaType *ret;
+
+    writeable_caps = gst_caps_copy(caps);
+    ret = transform_to_media_type(writeable_caps);
+
+    if (!(gst_caps_is_equal(caps, writeable_caps)))
+    {
+        IMFMediaType_Release(ret);
+        ret = NULL;
+    }
+    gst_caps_unref(writeable_caps);
+    return ret;
+}
+
+GstCaps *make_mf_compatible_caps(GstCaps *caps)
+{
+    GstCaps *ret;
+    IMFMediaType *media_type;
+
+    ret = gst_caps_copy(caps);
+
+    if ((media_type = transform_to_media_type(ret)))
+        IMFMediaType_Release(media_type);
+
+    if (!media_type)
+    {
+        gst_caps_unref(ret);
+        return NULL;
+    }
+
+    return ret;
+}
\ No newline at end of file
-- 
2.25.3

From 9de9aa62f2c40e583bf4c9c432f402f1d84a7e34 Mon Sep 17 00:00:00 2001
From: Derek Lesho <dlesho@codeweavers.com>
Date: Tue, 24 Mar 2020 16:00:26 -0500
Subject: [PATCH 11/46] winegstreamer: Translate H.264 caps to attributes.

Signed-off-by: Derek Lesho <dlesho@codeweavers.com>
---
 dlls/winegstreamer/mfplat.c | 67 +++++++++++++++++++++++++++++++++++++
 1 file changed, 67 insertions(+)

diff --git a/dlls/winegstreamer/mfplat.c b/dlls/winegstreamer/mfplat.c
index 09266ffab4..b76714c482 100644
--- a/dlls/winegstreamer/mfplat.c
+++ b/dlls/winegstreamer/mfplat.c
@@ -26,6 +26,7 @@
 #include "gst_private.h"
 #include "mfapi.h"
 #include "mfidl.h"
+#include "codecapi.h"
 
 #include "wine/debug.h"
 #include "wine/heap.h"
@@ -525,6 +526,72 @@ static IMFMediaType* transform_to_media_type(GstCaps *caps)
                     FIXME("Unrecognized format.\n");
             }
         }
+        else if (!(strcmp(mime_type, "video/x-h264")))
+        {
+            const char *profile, *level;
+
+            IMFMediaType_SetGUID(media_type, &MF_MT_SUBTYPE, &MFVideoFormat_H264);
+            IMFMediaType_SetUINT32(media_type, &MF_MT_COMPRESSED, TRUE);
+
+            if ((profile = gst_structure_get_string(info, "profile")))
+            {
+                if (!(strcmp(profile, "main")))
+                    IMFMediaType_SetUINT32(media_type, &MF_MT_MPEG2_PROFILE, eAVEncH264VProfile_Main);
+                else if (!(strcmp(profile, "high")))
+                    IMFMediaType_SetUINT32(media_type, &MF_MT_MPEG2_PROFILE, eAVEncH264VProfile_High);
+                else if (!(strcmp(profile, "high-4:4:4")))
+                    IMFMediaType_SetUINT32(media_type, &MF_MT_MPEG2_PROFILE, eAVEncH264VProfile_444);
+                else
+                    FIXME("Unrecognized profile %s\n", profile);
+            }
+            if ((level = gst_structure_get_string(info, "level")))
+            {
+                unsigned int i;
+
+                const static struct
+                {
+                    const char *name;
+                    enum eAVEncH264VLevel val;
+                } levels[] =
+                {
+                    {"1",   eAVEncH264VLevel1},
+                    {"1.1", eAVEncH264VLevel1_1},
+                    {"1.2", eAVEncH264VLevel1_2},
+                    {"1.3", eAVEncH264VLevel1_3},
+                    {"2",   eAVEncH264VLevel2},
+                    {"2.1", eAVEncH264VLevel2_1},
+                    {"2.2", eAVEncH264VLevel2_2},
+                    {"3",   eAVEncH264VLevel3},
+                    {"3.1", eAVEncH264VLevel3_1},
+                    {"3.2", eAVEncH264VLevel3_2},
+                    {"4",   eAVEncH264VLevel4},
+                    {"4.1", eAVEncH264VLevel4_1},
+                    {"4.2", eAVEncH264VLevel4_2},
+                    {"5",   eAVEncH264VLevel5},
+                    {"5.1", eAVEncH264VLevel5_1},
+                    {"5.2", eAVEncH264VLevel5_2},
+                };
+                for (i = 0 ; i < ARRAY_SIZE(levels); i++)
+                {
+                    if (!(strcmp(level, levels[i].name)))
+                    {
+                        IMFMediaType_SetUINT32(media_type, &MF_MT_MPEG2_LEVEL, levels[i].val);
+                        break;
+                    }
+                }
+                if (i == ARRAY_SIZE(levels))
+                {
+                    FIXME("Unrecognized level %s", level);
+                }
+            }
+            gst_caps_set_simple(caps, "stream-format", G_TYPE_STRING, "byte-stream", NULL);
+            gst_caps_set_simple(caps, "alignment", G_TYPE_STRING, "au", NULL);
+            for (unsigned int i = 0; i < gst_caps_get_size(caps); i++)
+            {
+                GstStructure *structure = gst_caps_get_structure (caps, i);
+                gst_structure_remove_field(structure, "codec_data");
+            }
+        }
         else
             FIXME("Unrecognized video format %s\n", mime_type);
     }
-- 
2.25.3

From 853cdb5d8dbfbdf20ddda6202f0236fb9442b6cd Mon Sep 17 00:00:00 2001
From: Derek Lesho <dlesho@codeweavers.com>
Date: Tue, 24 Mar 2020 16:01:20 -0500
Subject: [PATCH 12/46] winegstreamer: Translate WMV caps to attributes.

Signed-off-by: Derek Lesho <dlesho@codeweavers.com>
---
 dlls/winegstreamer/mfplat.c | 44 +++++++++++++++++++++++++++++++++++++
 1 file changed, 44 insertions(+)

diff --git a/dlls/winegstreamer/mfplat.c b/dlls/winegstreamer/mfplat.c
index b76714c482..1b0f562dbb 100644
--- a/dlls/winegstreamer/mfplat.c
+++ b/dlls/winegstreamer/mfplat.c
@@ -592,6 +592,50 @@ static IMFMediaType* transform_to_media_type(GstCaps *caps)
                 gst_structure_remove_field(structure, "codec_data");
             }
         }
+        else if (!(strcmp(mime_type, "video/x-wmv")))
+        {
+            gint wmv_version;
+            const char *format;
+            const GValue *codec_data;
+
+            if (gst_structure_get_int(info, "wmvversion", &wmv_version))
+            {
+                switch (wmv_version)
+                {
+                    case 1:
+                        IMFMediaType_SetGUID(media_type, &MF_MT_SUBTYPE, &MFVideoFormat_WMV1);
+                        break;
+                    case 2:
+                        IMFMediaType_SetGUID(media_type, &MF_MT_SUBTYPE, &MFVideoFormat_WMV2);
+                        break;
+                    case 3:
+                        IMFMediaType_SetGUID(media_type, &MF_MT_SUBTYPE, &MFVideoFormat_WMV3);
+                        break;
+                    default:
+                        FIXME("Unrecognized wmvversion %d\n", wmv_version);
+                }
+            }
+
+            if ((format = gst_structure_get_string(info, "format")))
+            {
+                if (!(strcmp(format, "WVC1")))
+                    IMFMediaType_SetGUID(media_type, &MF_MT_SUBTYPE, &MFVideoFormat_WVC1);
+                else
+                    FIXME("Unrecognized format %s\n", format);
+            }
+
+            if ((codec_data = gst_structure_get_value(info, "codec_data")))
+            {
+                GstBuffer *codec_data_buffer = gst_value_get_buffer(codec_data);
+                if (codec_data_buffer)
+                {
+                    gsize codec_data_size = gst_buffer_get_size(codec_data_buffer);
+                    gpointer codec_data_raw = heap_alloc(codec_data_size);
+                    gst_buffer_extract(codec_data_buffer, 0, codec_data_raw, codec_data_size);
+                    IMFMediaType_SetBlob(media_type, &MF_MT_USER_DATA, codec_data_raw, codec_data_size);
+                }
+            }
+        }
         else
             FIXME("Unrecognized video format %s\n", mime_type);
     }
-- 
2.25.3

From 3b5861bcd4cae45ab576ec30b21f88e1a3ac04d6 Mon Sep 17 00:00:00 2001
From: Derek Lesho <dlesho@codeweavers.com>
Date: Tue, 24 Mar 2020 16:02:27 -0500
Subject: [PATCH 13/46] winegstreamer: Translate AAC caps to attributes.

Signed-off-by: Derek Lesho <dlesho@codeweavers.com>
---
 dlls/winegstreamer/mfplat.c | 118 ++++++++++++++++++++++++++++++++++++
 1 file changed, 118 insertions(+)

diff --git a/dlls/winegstreamer/mfplat.c b/dlls/winegstreamer/mfplat.c
index 1b0f562dbb..ca248f3f35 100644
--- a/dlls/winegstreamer/mfplat.c
+++ b/dlls/winegstreamer/mfplat.c
@@ -463,6 +463,15 @@ uncompressed_formats[] =
     {&MFVideoFormat_RGB555,  GST_VIDEO_FORMAT_BGR15},
 };
 
+struct aac_user_data
+{
+    WORD payload_type;
+    WORD profile_level_indication;
+    WORD struct_type;
+    WORD reserved;
+  /* audio-specific-config is stored here */
+};
+
 /* caps will be modified to represent the exact type needed for the format */
 static IMFMediaType* transform_to_media_type(GstCaps *caps)
 {
@@ -691,6 +700,115 @@ static IMFMediaType* transform_to_media_type(GstCaps *caps)
                 ERR("Failed to get audio format\n");
             }
         }
+        else if (!(strcmp(mime_type, "audio/mpeg")))
+        {
+            int mpeg_version = -1;
+
+            IMFMediaType_SetUINT32(media_type, &MF_MT_COMPRESSED, TRUE);
+
+            if (!(gst_structure_get_int(info, "mpegversion", &mpeg_version)))
+                ERR("Failed to get mpegversion\n");
+            switch (mpeg_version)
+            {
+                case 2:
+                case 4:
+                {
+                    const char *format, *profile, *level;
+                    DWORD profile_level_indication = 0;
+                    const GValue *codec_data;
+                    DWORD asc_size = 0;
+                    struct aac_user_data *user_data = NULL;
+
+                    IMFMediaType_SetGUID(media_type, &MF_MT_SUBTYPE, &MFAudioFormat_AAC);
+
+                    codec_data = gst_structure_get_value(info, "codec_data");
+                    if (codec_data)
+                    {
+                        GstBuffer *codec_data_buffer = gst_value_get_buffer(codec_data);
+                        if (codec_data_buffer)
+                        {
+                            if ((asc_size = gst_buffer_get_size(codec_data_buffer)) >= 2)
+                            {
+                                user_data = heap_alloc_zero(sizeof(*user_data)+asc_size);
+                                gst_buffer_extract(codec_data_buffer, 0, (gpointer)(user_data + 1), asc_size);
+                            }
+                            else
+                                ERR("Unexpected buffer size\n");
+                        }
+                        else
+                            ERR("codec_data not a buffer\n");
+                    }
+                    else
+                        ERR("codec_data not found\n");
+                    if (!user_data)
+                        user_data = heap_alloc_zero(sizeof(*user_data));
+
+                    {
+                        int rate;
+                        if (gst_structure_get_int(info, "rate", &rate))
+                            IMFMediaType_SetUINT32(media_type, &MF_MT_AUDIO_SAMPLES_PER_SECOND, rate);
+                    }
+                    {
+                        int channels;
+                        if (gst_structure_get_int(info, "channels", &channels))
+                            IMFMediaType_SetUINT32(media_type, &MF_MT_AUDIO_NUM_CHANNELS, channels);
+                    }
+
+                    if ((format = gst_structure_get_string(info, "stream-format")))
+                    {
+                        DWORD payload_type = -1;
+                        if (!(strcmp(format, "raw")))
+                            payload_type = 0;
+                        else if (!(strcmp(format, "adts")))
+                            payload_type = 1;
+                        else if (!(strcmp(format, "adif")))
+                            payload_type = 2;
+                        else if (!(strcmp(format, "loas")))
+                            payload_type = 3;
+                        else
+                            FIXME("Unrecognized stream-format\n");
+                        if (payload_type != -1)
+                        {
+                            IMFMediaType_SetUINT32(media_type, &MF_MT_AAC_PAYLOAD_TYPE, payload_type);
+                            user_data->payload_type = payload_type;
+                        }
+                    }
+                    else
+                    {
+                        ERR("Stream format not present\n");
+                    }
+
+                    profile = gst_structure_get_string(info, "profile");
+                    level = gst_structure_get_string(info, "level");
+                    /* Data from http://archive.is/whp6P#45% */
+                    if (profile && level)
+                    {
+                        if (!(strcmp(profile, "lc")) && !(strcmp(level, "2")))
+                            profile_level_indication = 0x29;
+                        else if (!(strcmp(profile, "lc")) && !(strcmp(level, "4")))
+                            profile_level_indication = 0x2A;
+                        else if (!(strcmp(profile, "lc")) && !(strcmp(level, "5")))
+                            profile_level_indication = 0x2B;
+                        else
+                            FIXME("Unhandled profile/level combo\n");
+                    }
+                    else
+                        ERR("Profile or level not present\n");
+
+                    if (profile_level_indication)
+                    {
+                        IMFMediaType_SetUINT32(media_type, &MF_MT_AAC_AUDIO_PROFILE_LEVEL_INDICATION, profile_level_indication);
+                        user_data->profile_level_indication = profile_level_indication;
+                    }
+
+                    IMFMediaType_SetBlob(media_type, &MF_MT_USER_DATA, (BYTE *)user_data, sizeof(*user_data) + asc_size);
+                    heap_free(user_data);
+                    break;
+                }
+                default:
+                    FIXME("Unhandled mpegversion %d\n", mpeg_version);
+            }
+        }
         else
             FIXME("Unrecognized audio format %s\n", mime_type);
     }
-- 
2.25.3

From 4eb0f9f8f9e507d481302444c203cbd16657c7b3 Mon Sep 17 00:00:00 2001
From: Derek Lesho <dlesho@codeweavers.com>
Date: Wed, 25 Mar 2020 13:36:19 -0500
Subject: [PATCH 14/46] winegstreamer: Add video/mpeg recognition.

Signed-off-by: Derek Lesho <dlesho@codeweavers.com>
---
 dlls/winegstreamer/mfplat.c | 15 +++++++++++++++
 1 file changed, 15 insertions(+)

diff --git a/dlls/winegstreamer/mfplat.c b/dlls/winegstreamer/mfplat.c
index ca248f3f35..c74f956339 100644
--- a/dlls/winegstreamer/mfplat.c
+++ b/dlls/winegstreamer/mfplat.c
@@ -645,6 +645,21 @@ static IMFMediaType* transform_to_media_type(GstCaps *caps)
                 }
             }
         }
+        else if (!(strcmp(mime_type, "video/mpeg")))
+        {
+            gint mpegversion;
+            if (gst_structure_get_int(info, "mpegversion", &mpegversion))
+            {
+                switch (mpegversion)
+                {
+                    case 1: IMFMediaType_SetGUID(media_type, &MF_MT_SUBTYPE, &MFVideoFormat_MPG1); break;
+                    case 2: IMFMediaType_SetGUID(media_type, &MF_MT_SUBTYPE, &MFVideoFormat_MPEG2); break;
+                    case 4: IMFMediaType_SetGUID(media_type, &MF_MT_SUBTYPE, &MFVideoFormat_M4S2); break;
+                    default: FIXME("Unrecognized mpeg version %d\n", mpegversion);
+                }
+            }
+            IMFMediaType_SetUINT32(media_type, &MF_MT_COMPRESSED, TRUE);
+        }
         else
             FIXME("Unrecognized video format %s\n", mime_type);
     }
-- 
2.25.3

From 1033d8d2e797dcf45f069668f44acfcaafb9f0dd Mon Sep 17 00:00:00 2001
From: Derek Lesho <dlesho@codeweavers.com>
Date: Tue, 31 Mar 2020 11:21:21 -0500
Subject: [PATCH 15/46] winegstreamer: Implement
 IMFMediaSource::CreatePresentationDescriptor.

Signed-off-by: Derek Lesho <dlesho@codeweavers.com>
---
 dlls/mfplat/tests/mfplat.c        |  8 +++----
 dlls/winegstreamer/media_source.c | 36 +++++++++++++++++++++++++++++--
 2 files changed, 38 insertions(+), 6 deletions(-)

diff --git a/dlls/mfplat/tests/mfplat.c b/dlls/mfplat/tests/mfplat.c
index 6381a93bd1..0761d28d90 100644
--- a/dlls/mfplat/tests/mfplat.c
+++ b/dlls/mfplat/tests/mfplat.c
@@ -494,10 +494,7 @@ static void test_source_resolver(void)
     ok(obj_type == MF_OBJECT_MEDIASOURCE, "got %d\n", obj_type);
 
     hr = IMFMediaSource_CreatePresentationDescriptor(mediasource, &descriptor);
-todo_wine
     ok(hr == S_OK, "Failed to get presentation descriptor, hr %#x.\n", hr);
-    if (FAILED(hr))
-        goto skip_source_tests;
     ok(descriptor != NULL, "got %p\n", descriptor);
 
     hr = IMFPresentationDescriptor_GetStreamDescriptorByIndex(descriptor, 0, &selected, &sd);
@@ -525,7 +522,10 @@ todo_wine
 
     var.vt = VT_EMPTY;
     hr = IMFMediaSource_Start(mediasource, descriptor, &GUID_NULL, &var);
+todo_wine
     ok(hr == S_OK, "Failed to start media source, hr %#x.\n", hr);
+    if (FAILED(hr))
+        goto skip_source_tests;
 
     get_event((IMFMediaEventGenerator *)mediasource, MENewStream, &var);
     ok(var.vt == VT_UNKNOWN, "Unexpected value type %u from MENewStream event.\n", var.vt);
@@ -588,10 +588,10 @@ todo_wine
 
     get_event((IMFMediaEventGenerator *)mediasource, MEEndOfPresentation, NULL);
 
+skip_source_tests:
     IMFMediaTypeHandler_Release(handler);
     IMFPresentationDescriptor_Release(descriptor);
 
-skip_source_tests:
     hr = IMFMediaSource_Shutdown(mediasource);
     ok(hr == S_OK, "Unexpected hr %#x.\n", hr);
 
diff --git a/dlls/winegstreamer/media_source.c b/dlls/winegstreamer/media_source.c
index a654d555bc..04b3b9f6c7 100644
--- a/dlls/winegstreamer/media_source.c
+++ b/dlls/winegstreamer/media_source.c
@@ -61,6 +61,7 @@ struct media_source
     IMFByteStream *byte_stream;
     struct media_stream **streams;
     ULONG stream_count;
+    IMFPresentationDescriptor *pres_desc;
     GstBus *bus;
     GstElement *container;
     GstElement *demuxer;
@@ -742,12 +743,19 @@ static HRESULT WINAPI media_source_CreatePresentationDescriptor(IMFMediaSource *
 {
     struct media_source *source = impl_from_IMFMediaSource(iface);
 
-    FIXME("(%p)->(%p): stub\n", source, descriptor);
+    TRACE("(%p)->(%p)\n", source, descriptor);
 
     if (source->state == SOURCE_SHUTDOWN)
         return MF_E_SHUTDOWN;
 
-    return E_NOTIMPL;
+    if (!(source->pres_desc))
+    {
+        return MF_E_NOT_INITIALIZED;
+    }
+
+    IMFPresentationDescriptor_Clone(source->pres_desc, descriptor);
+
+    return S_OK;
 }
 
 static HRESULT WINAPI media_source_Start(IMFMediaSource *iface, IMFPresentationDescriptor *descriptor,
@@ -798,6 +806,8 @@ static HRESULT media_source_teardown(struct media_source *source)
         gst_element_set_state(source->container, GST_STATE_NULL);
         gst_object_unref(GST_OBJECT(source->container));
     }
+    if (source->pres_desc)
+        IMFPresentationDescriptor_Release(source->pres_desc);
     if (source->event_queue)
         IMFMediaEventQueue_Release(source->event_queue);
     if (source->byte_stream)
@@ -856,6 +866,7 @@ static HRESULT media_source_constructor(IMFByteStream *bytestream, enum source_t
     struct media_source *object = heap_alloc_zero(sizeof(*object));
     GList *demuxer_list_one, *demuxer_list_two;
     GstElementFactory *demuxer_factory = NULL;
+    IMFStreamDescriptor **descriptors = NULL;
     HRESULT hr;
     int ret;
 
@@ -939,6 +950,25 @@ static HRESULT media_source_constructor(IMFByteStream *bytestream, enum source_t
 
     WaitForSingleObject(object->all_streams_event, INFINITE);
 
+    /* init presentation descriptor */
+
+    descriptors = heap_alloc(object->stream_count * sizeof(IMFStreamDescriptor*));
+    for (unsigned int i = 0; i < object->stream_count; i++)
+    {
+        IMFMediaStream_GetStreamDescriptor(&object->streams[i]->IMFMediaStream_iface, &descriptors[i]);
+    }
+
+    if (FAILED(MFCreatePresentationDescriptor(object->stream_count, descriptors, &object->pres_desc)))
+        goto fail;
+
+    for (unsigned int i = 0; i < object->stream_count; i++)
+    {
+        IMFPresentationDescriptor_SelectStream(object->pres_desc, i);
+        IMFStreamDescriptor_Release(descriptors[i]);
+    }
+    heap_free(descriptors);
+    descriptors = NULL;
+
     gst_element_set_state(object->container, GST_STATE_READY);
 
     object->state = SOURCE_STOPPED;
@@ -951,6 +981,8 @@ static HRESULT media_source_constructor(IMFByteStream *bytestream, enum source_t
 
     if (demuxer_factory)
         gst_object_unref(demuxer_factory);
+    if (descriptors)
+        heap_free(descriptors);
     media_source_teardown(object);
     heap_free(object);
     *out_media_source = NULL;
-- 
2.25.3

From 699ca49f99fff0c00435654d00c67cc4d224510f Mon Sep 17 00:00:00 2001
From: Derek Lesho <dlesho@codeweavers.com>
Date: Tue, 31 Mar 2020 15:10:03 -0500
Subject: [PATCH 16/46] winegstreamer: Implement IMFMediaSource::Start.

Signed-off-by: Derek Lesho <dlesho@codeweavers.com>
---
 dlls/mfplat/tests/mfplat.c        |   8 +-
 dlls/winegstreamer/media_source.c | 283 +++++++++++++++++++++++++++++-
 2 files changed, 284 insertions(+), 7 deletions(-)

diff --git a/dlls/mfplat/tests/mfplat.c b/dlls/mfplat/tests/mfplat.c
index 0761d28d90..23db0e3abe 100644
--- a/dlls/mfplat/tests/mfplat.c
+++ b/dlls/mfplat/tests/mfplat.c
@@ -522,10 +522,7 @@ static void test_source_resolver(void)
 
     var.vt = VT_EMPTY;
     hr = IMFMediaSource_Start(mediasource, descriptor, &GUID_NULL, &var);
-todo_wine
     ok(hr == S_OK, "Failed to start media source, hr %#x.\n", hr);
-    if (FAILED(hr))
-        goto skip_source_tests;
 
     get_event((IMFMediaEventGenerator *)mediasource, MENewStream, &var);
     ok(var.vt == VT_UNKNOWN, "Unexpected value type %u from MENewStream event.\n", var.vt);
@@ -543,10 +540,13 @@ todo_wine
         hr = IMFMediaStream_RequestSample(video_stream, NULL);
         if (i == sample_count)
             break;
+todo_wine
         ok(hr == S_OK, "Failed to request sample %u, hr %#x.\n", i + 1, hr);
         if (hr != S_OK)
             break;
     }
+    if (FAILED(hr))
+        goto skip_source_tests;
 
     for (i = 0; i < sample_count; ++i)
     {
@@ -584,11 +584,11 @@ todo_wine
 
     hr = IMFMediaStream_RequestSample(video_stream, NULL);
     ok(hr == MF_E_END_OF_STREAM, "Unexpected hr %#x.\n", hr);
-    IMFMediaStream_Release(video_stream);
 
     get_event((IMFMediaEventGenerator *)mediasource, MEEndOfPresentation, NULL);
 
 skip_source_tests:
+    IMFMediaStream_Release(video_stream);
     IMFMediaTypeHandler_Release(handler);
     IMFPresentationDescriptor_Release(descriptor);
 
diff --git a/dlls/winegstreamer/media_source.c b/dlls/winegstreamer/media_source.c
index 04b3b9f6c7..abf0cfd56f 100644
--- a/dlls/winegstreamer/media_source.c
+++ b/dlls/winegstreamer/media_source.c
@@ -50,12 +50,36 @@ struct media_stream
         STREAM_RUNNING,
         STREAM_SHUTDOWN,
     } state;
+    BOOL eos;
+};
+
+enum source_async_op
+{
+    SOURCE_ASYNC_START,
+};
+
+struct source_async_command
+{
+    IUnknown IUnknown_iface;
+    LONG refcount;
+    enum source_async_op op;
+    union
+    {
+        struct
+        {
+            IMFPresentationDescriptor *descriptor;
+            GUID format;
+            PROPVARIANT position;
+        } start;
+    } u;
 };
 
 struct media_source
 {
     IMFMediaSource IMFMediaSource_iface;
+    IMFAsyncCallback async_commands_callback;
     LONG ref;
+    DWORD async_commands_queue;
     enum source_type type;
     IMFMediaEventQueue *event_queue;
     IMFByteStream *byte_stream;
@@ -87,6 +111,236 @@ static inline struct media_source *impl_from_IMFMediaSource(IMFMediaSource *ifac
     return CONTAINING_RECORD(iface, struct media_source, IMFMediaSource_iface);
 }
 
+static inline struct media_source *impl_from_async_commands_callback_IMFAsyncCallback(IMFAsyncCallback *iface)
+{
+    return CONTAINING_RECORD(iface, struct media_source, async_commands_callback);
+}
+
+static inline struct source_async_command *impl_from_async_command_IUnknown(IUnknown *iface)
+{
+    return CONTAINING_RECORD(iface, struct source_async_command, IUnknown_iface);
+}
+
+static HRESULT WINAPI source_async_command_QueryInterface(IUnknown *iface, REFIID riid, void **obj)
+{
+    if (IsEqualIID(riid, &IID_IUnknown))
+    {
+        *obj = iface;
+        IUnknown_AddRef(iface);
+        return S_OK;
+    }
+
+    WARN("Unsupported interface %s.\n", debugstr_guid(riid));
+    *obj = NULL;
+    return E_NOINTERFACE;
+}
+
+static ULONG WINAPI source_async_command_AddRef(IUnknown *iface)
+{
+    struct source_async_command *command = impl_from_async_command_IUnknown(iface);
+    return InterlockedIncrement(&command->refcount);
+}
+
+static ULONG WINAPI source_async_command_Release(IUnknown *iface)
+{
+    struct source_async_command *command = impl_from_async_command_IUnknown(iface);
+    ULONG refcount = InterlockedDecrement(&command->refcount);
+
+    if (!refcount)
+    {
+        if (command->op == SOURCE_ASYNC_START)
+            PropVariantClear(&command->u.start.position);
+        heap_free(command);
+    }
+
+    return refcount;
+}
+
+static const IUnknownVtbl source_async_command_vtbl =
+{
+    source_async_command_QueryInterface,
+    source_async_command_AddRef,
+    source_async_command_Release,
+};
+
+static HRESULT source_create_async_op(enum source_async_op op, struct source_async_command **ret)
+{
+    struct source_async_command *command;
+
+    if (!(command = heap_alloc_zero(sizeof(*command))))
+        return E_OUTOFMEMORY;
+
+    command->IUnknown_iface.lpVtbl = &source_async_command_vtbl;
+    command->op = op;
+
+    *ret = command;
+
+    return S_OK;
+}
+
+static HRESULT WINAPI callback_QueryInterface(IMFAsyncCallback *iface, REFIID riid, void **obj)
+{
+    TRACE("%p, %s, %p.\n", iface, debugstr_guid(riid), obj);
+
+    if (IsEqualIID(riid, &IID_IMFAsyncCallback) ||
+            IsEqualIID(riid, &IID_IUnknown))
+    {
+        *obj = iface;
+        IMFAsyncCallback_AddRef(iface);
+        return S_OK;
+    }
+
+    WARN("Unsupported %s.\n", debugstr_guid(riid));
+    *obj = NULL;
+    return E_NOINTERFACE;
+}
+
+static HRESULT WINAPI callback_GetParameters(IMFAsyncCallback *iface,
+        DWORD *flags, DWORD *queue)
+{
+    return E_NOTIMPL;
+}
+
+static ULONG WINAPI source_async_commands_callback_AddRef(IMFAsyncCallback *iface)
+{
+    struct media_source *source = impl_from_async_commands_callback_IMFAsyncCallback(iface);
+    return IMFMediaSource_AddRef(&source->IMFMediaSource_iface);
+}
+
+static ULONG WINAPI source_async_commands_callback_Release(IMFAsyncCallback *iface)
+{
+    struct media_source *source = impl_from_async_commands_callback_IMFAsyncCallback(iface);
+    return IMFMediaSource_Release(&source->IMFMediaSource_iface);
+}
+
+static IMFStreamDescriptor *stream_descriptor_from_id(IMFPresentationDescriptor *pres_desc, DWORD id, BOOL *selected)
+{
+    ULONG sd_count;
+    IMFStreamDescriptor *ret;
+
+    if (FAILED(IMFPresentationDescriptor_GetStreamDescriptorCount(pres_desc, &sd_count)))
+        return NULL;
+
+    for (unsigned int i = 0; i < sd_count; i++)
+    {
+        DWORD stream_id;
+
+        if (FAILED(IMFPresentationDescriptor_GetStreamDescriptorByIndex(pres_desc, i, selected, &ret)))
+            return NULL;
+
+        if (SUCCEEDED(IMFStreamDescriptor_GetStreamIdentifier(ret, &stream_id)) && stream_id == id)
+            return ret;
+
+        IMFStreamDescriptor_Release(ret);
+    }
+    return NULL;
+}
+
+static HRESULT start_pipeline(struct media_source *source, struct source_async_command *command)
+{
+    PROPVARIANT *position = &command->u.start.position;
+    BOOL seek_message = source->state != SOURCE_STOPPED && position->vt != VT_EMPTY;
+
+    /* we can't seek until the pipeline is in a valid state */
+    gst_element_set_state(source->container, GST_STATE_PAUSED);
+    assert(gst_element_get_state(source->container, NULL, NULL, -1) == GST_STATE_CHANGE_SUCCESS);
+    if (source->state == SOURCE_STOPPED)
+    {
+        WaitForSingleObject(source->all_streams_event, INFINITE);
+    }
+
+    for (unsigned int i = 0; i < source->stream_count; i++)
+    {
+        struct media_stream *stream;
+        IMFStreamDescriptor *sd;
+        DWORD stream_id;
+        BOOL was_active;
+        BOOL selected;
+
+        stream = source->streams[i];
+
+        IMFStreamDescriptor_GetStreamIdentifier(stream->descriptor, &stream_id);
+
+        sd = stream_descriptor_from_id(command->u.start.descriptor, stream_id, &selected);
+        IMFStreamDescriptor_Release(sd);
+
+        was_active = stream->state != STREAM_INACTIVE;
+
+        if (position->vt != VT_EMPTY)
+        {
+            GstEvent *seek_event = gst_event_new_seek(1.0, GST_FORMAT_TIME, GST_SEEK_FLAG_FLUSH,
+                    GST_SEEK_TYPE_SET, position->u.hVal.QuadPart / 100, GST_SEEK_TYPE_NONE, 0);
+
+
+            gst_pad_push_event(stream->my_sink, seek_event);
+            gst_bus_poll(source->bus, GST_MESSAGE_RESET_TIME, -1);
+
+            stream->eos = FALSE;
+        }
+
+        stream->state = selected ? STREAM_RUNNING : STREAM_INACTIVE;
+
+        if (selected)
+        {
+            TRACE("Stream %u (%p) selected\n", i, stream);
+            IMFMediaEventQueue_QueueEventParamUnk(source->event_queue,
+            was_active ? MEUpdatedStream : MENewStream, &GUID_NULL,
+            S_OK, (IUnknown*) &stream->IMFMediaStream_iface);
+
+            IMFMediaEventQueue_QueueEventParamVar(stream->event_queue,
+                seek_message ? MEStreamSeeked : MEStreamStarted, &GUID_NULL, S_OK, position);
+        }
+    }
+
+    IMFMediaEventQueue_QueueEventParamVar(source->event_queue,
+        seek_message ? MESourceSeeked : MESourceStarted,
+        &GUID_NULL, S_OK, position);
+
+    source->state = SOURCE_RUNNING;
+
+    gst_element_set_state(source->container, GST_STATE_PLAYING);
+    gst_element_get_state(source->container, NULL, NULL, -1);
+
+    return S_OK;
+}
+
+static HRESULT WINAPI source_async_commands_Invoke(IMFAsyncCallback *iface, IMFAsyncResult *result)
+{
+    struct media_source *source = impl_from_async_commands_callback_IMFAsyncCallback(iface);
+    struct source_async_command *command;
+    IUnknown *state;
+    HRESULT hr;
+
+    if (source->state == SOURCE_SHUTDOWN)
+        return S_OK;
+
+    if (FAILED(hr = IMFAsyncResult_GetState(result, &state)))
+        return hr;
+
+    command = impl_from_async_command_IUnknown(state);
+    switch (command->op)
+    {
+        case SOURCE_ASYNC_START:
+            start_pipeline(source, command);
+            break;
+        default:
+            ;
+    }
+
+    IUnknown_Release(state);
+
+    return S_OK;
+}
+
+static const IMFAsyncCallbackVtbl source_async_commands_callback_vtbl =
+{
+    callback_QueryInterface,
+    source_async_commands_callback_AddRef,
+    source_async_commands_callback_Release,
+    callback_GetParameters,
+    source_async_commands_Invoke,
+};
+
 /* inactive stream sample discarder */
 static GstFlowReturn stream_new_sample(GstElement *appsink, gpointer user)
 {
@@ -759,16 +1013,32 @@ static HRESULT WINAPI media_source_CreatePresentationDescriptor(IMFMediaSource *
 }
 
 static HRESULT WINAPI media_source_Start(IMFMediaSource *iface, IMFPresentationDescriptor *descriptor,
-                                     const GUID *time_format, const PROPVARIANT *start_position)
+                                     const GUID *time_format, const PROPVARIANT *position)
 {
     struct media_source *source = impl_from_IMFMediaSource(iface);
+    struct source_async_command *command;
+    HRESULT hr;
 
-    FIXME("(%p)->(%p, %p, %p): stub\n", source, descriptor, time_format, start_position);
+    TRACE("(%p)->(%p, %p, %p)\n", source, descriptor, time_format, position);
 
     if (source->state == SOURCE_SHUTDOWN)
         return MF_E_SHUTDOWN;
 
-    return E_NOTIMPL;
+    if (!(IsEqualIID(time_format, &GUID_NULL)))
+    {
+        return MF_E_UNSUPPORTED_TIME_FORMAT;
+    }
+
+    if (SUCCEEDED(hr = source_create_async_op(SOURCE_ASYNC_START, &command)))
+    {
+        command->u.start.descriptor = descriptor;
+        command->u.start.format = *time_format;
+        PropVariantCopy(&command->u.start.position, position);
+
+        hr = MFPutWorkItem(source->async_commands_queue, &source->async_commands_callback, &command->IUnknown_iface);
+    }
+
+    return hr;
 }
 
 static HRESULT WINAPI media_source_Stop(IMFMediaSource *iface)
@@ -825,6 +1095,9 @@ static HRESULT media_source_teardown(struct media_source *source)
     if (source->all_streams_event)
         CloseHandle(source->all_streams_event);
 
+    if (source->async_commands_queue)
+        MFUnlockWorkQueue(source->async_commands_queue);
+
     return S_OK;
 }
 
@@ -874,6 +1147,7 @@ static HRESULT media_source_constructor(IMFByteStream *bytestream, enum source_t
         return E_OUTOFMEMORY;
 
     object->IMFMediaSource_iface.lpVtbl = &IMFMediaSource_vtbl;
+    object->async_commands_callback.lpVtbl = &source_async_commands_callback_vtbl;
     object->ref = 1;
     object->type = type;
     object->byte_stream = bytestream;
@@ -883,6 +1157,9 @@ static HRESULT media_source_constructor(IMFByteStream *bytestream, enum source_t
     if (FAILED(hr = MFCreateEventQueue(&object->event_queue)))
         goto fail;
 
+    if (FAILED(hr = MFAllocateWorkQueue(&object->async_commands_queue)))
+        goto fail;
+
     object->container = gst_bin_new(NULL);
     object->bus = gst_bus_new();
     gst_bus_set_sync_handler(object->bus, watch_source_bus_wrapper, object, NULL);
-- 
2.25.3

From 823feef9762735db059fca378a6b1bc08028e543 Mon Sep 17 00:00:00 2001
From: Derek Lesho <dlesho@codeweavers.com>
Date: Tue, 31 Mar 2020 15:11:31 -0500
Subject: [PATCH 17/46] winegstreamer: Insert parser into pipeline to rectify
 type differences.

It's probably better to use caps negotiation here, but this seemed simpler for now.

Signed-off-by: Derek Lesho <dlesho@codeweavers.com>
---
 dlls/winegstreamer/media_source.c | 114 ++++++++++++++++++++++++------
 1 file changed, 91 insertions(+), 23 deletions(-)

diff --git a/dlls/winegstreamer/media_source.c b/dlls/winegstreamer/media_source.c
index abf0cfd56f..cb247adcef 100644
--- a/dlls/winegstreamer/media_source.c
+++ b/dlls/winegstreamer/media_source.c
@@ -578,7 +578,6 @@ static void source_stream_added(GstElement *element, GstPad *pad, gpointer user)
             {
                 ERR("Error linking demuxer to stream %p, err = %d\n", existing_stream, ret);
             }
-            gst_element_sync_state_with_parent(existing_stream->appsink);
 
             goto leave;
         }
@@ -794,12 +793,99 @@ static const IMFMediaStreamVtbl media_stream_vtbl =
     media_stream_RequestSample
 };
 
+/* TODO: Use gstreamer caps negotiation */
+/* connects their_src to appsink */
+static HRESULT media_stream_align_with_mf(struct media_stream *stream, IMFMediaType **stream_type)
+{
+    GstCaps *source_caps = NULL;
+    GstCaps *target_caps = NULL;
+    GstElement *parser = NULL;
+    HRESULT hr = E_FAIL;
+
+    if (!(source_caps = gst_pad_query_caps(stream->their_src, NULL)))
+        goto done;
+    if (!(target_caps = make_mf_compatible_caps(source_caps)))
+        goto done;
+
+    if (TRACE_ON(mfplat))
+    {
+        gchar *source_caps_str = gst_caps_to_string(source_caps), *target_caps_str = gst_caps_to_string(target_caps);
+        TRACE("source caps %s\ntarget caps %s\n", debugstr_a(source_caps_str), debugstr_a(target_caps_str));
+        g_free(source_caps_str);
+        g_free(target_caps_str);
+    }
+
+    g_object_set(stream->appsink, "caps", target_caps, NULL);
+
+    if (!(gst_caps_is_equal(source_caps, target_caps)))
+    {
+        GList *parser_list_one, *parser_list_two;
+        GstElementFactory *parser_factory;
+
+        parser_list_one = gst_element_factory_list_get_elements(GST_ELEMENT_FACTORY_TYPE_PARSER, 1);
+
+        parser_list_two = gst_element_factory_list_filter(parser_list_one, source_caps, GST_PAD_SINK, 0);
+        gst_plugin_feature_list_free(parser_list_one);
+        parser_list_one = parser_list_two;
+
+        parser_list_two = gst_element_factory_list_filter(parser_list_one, target_caps, GST_PAD_SRC, 0);
+        gst_plugin_feature_list_free(parser_list_one);
+        parser_list_one = parser_list_two;
+
+        if (!(g_list_length(parser_list_one)))
+        {
+            gst_plugin_feature_list_free(parser_list_one);
+            ERR("Failed to find parser for stream\n");
+            hr = E_FAIL;
+            goto done;
+        }
+
+        parser_factory = g_list_first(parser_list_one)->data;
+        TRACE("Found parser %s.\n", GST_ELEMENT_NAME(parser_factory));
+
+        parser = gst_element_factory_create(parser_factory, NULL);
+
+        gst_plugin_feature_list_free(parser_list_one);
+
+        if (!parser)
+        {
+            hr = E_FAIL;
+            goto done;
+        }
+
+        gst_bin_add(GST_BIN(stream->parent_source->container), parser);
+
+        assert(gst_pad_link(stream->their_src, gst_element_get_static_pad(parser, "sink")) == GST_PAD_LINK_OK);
+
+        assert(gst_element_link(parser, stream->appsink));
+
+        gst_element_sync_state_with_parent(parser);
+    }
+    else
+    {
+        assert(gst_pad_link(stream->their_src, gst_element_get_static_pad(stream->appsink, "sink")) == GST_PAD_LINK_OK);
+    }
+
+    stream->my_sink = gst_element_get_static_pad(parser ? parser : stream->appsink, "sink");
+
+    *stream_type = mf_media_type_from_caps(target_caps);
+
+    hr = S_OK;
+
+    done:
+    if (source_caps)
+        gst_caps_unref(source_caps);
+    if (target_caps)
+        gst_caps_unref(target_caps);
+
+    return hr;
+}
+
 static HRESULT media_stream_constructor(struct media_source *source, GstPad *pad, DWORD stream_id, struct media_stream **out_stream)
 {
     struct media_stream *object = heap_alloc_zero(sizeof(*object));
     IMFMediaTypeHandler *type_handler = NULL;
     IMFMediaType *stream_type = NULL;
-    GstCaps *caps = NULL;
     HRESULT hr;
 
     TRACE("(%p %p)->(%p)\n", source, pad, out_stream);
@@ -828,22 +914,9 @@ static HRESULT media_stream_constructor(struct media_source *source, GstPad *pad
     g_object_set(object->appsink, "async", FALSE, NULL); /* <- This allows us interact with the bin w/o prerolling */
     g_signal_connect(object->appsink, "new-sample", G_CALLBACK(stream_new_sample_wrapper), object);
 
-    if (!(caps = gst_pad_query_caps(pad, NULL)))
+    if (FAILED(hr = media_stream_align_with_mf(object, &stream_type)))
         goto fail;
 
-    if (TRACE_ON(mfplat))
-    {
-        gchar *caps_str = gst_caps_to_string(caps);
-        TRACE("caps %s\n", debugstr_a(caps_str));
-        g_free(caps_str);
-    }
-
-    if (!(stream_type = mf_media_type_from_caps(caps)))
-        goto fail;
-
-    gst_caps_unref(caps);
-    caps = NULL;
-
     if (FAILED(hr = MFCreateStreamDescriptor(stream_id, 1, &stream_type, &object->descriptor)))
         goto fail;
 
@@ -858,23 +931,18 @@ static HRESULT media_stream_constructor(struct media_source *source, GstPad *pad
     IMFMediaType_Release(stream_type);
     stream_type = NULL;
 
-    object->my_sink = gst_element_get_static_pad(object->appsink, "sink");
-    gst_pad_set_element_private(pad, object);
-
-    gst_pad_link(object->their_src, object->my_sink);
+    gst_pad_set_element_private(object->their_src, object);
 
     gst_element_sync_state_with_parent(object->appsink);
 
     TRACE("->(%p)\n", object);
-
     *out_stream = object;
+
     return S_OK;
 
     fail:
     WARN("Failed to construct media stream, hr %#x.\n", hr);
 
-    if (caps)
-        gst_caps_unref(caps);
     if (stream_type)
         IMFMediaType_Release(stream_type);
     if (type_handler)
-- 
2.25.3

From c6976f8ea8dc4f8a1192b281aa98f97286434277 Mon Sep 17 00:00:00 2001
From: Derek Lesho <dlesho@codeweavers.com>
Date: Wed, 25 Mar 2020 10:43:03 -0500
Subject: [PATCH 18/46] winegstreamer: Implement IMFMediaStream::RequestSample.

Signed-off-by: Derek Lesho <dlesho@codeweavers.com>
---
 dlls/mfplat/tests/mfplat.c        |   4 --
 dlls/winegstreamer/gst_private.h  |   1 +
 dlls/winegstreamer/media_source.c | 116 ++++++++++++++++++++++++++++--
 dlls/winegstreamer/mfplat.c       |  90 +++++++++++++++++++++++
 4 files changed, 202 insertions(+), 9 deletions(-)

diff --git a/dlls/mfplat/tests/mfplat.c b/dlls/mfplat/tests/mfplat.c
index 23db0e3abe..f3d9bd8ab2 100644
--- a/dlls/mfplat/tests/mfplat.c
+++ b/dlls/mfplat/tests/mfplat.c
@@ -540,13 +540,10 @@ static void test_source_resolver(void)
         hr = IMFMediaStream_RequestSample(video_stream, NULL);
         if (i == sample_count)
             break;
-todo_wine
         ok(hr == S_OK, "Failed to request sample %u, hr %#x.\n", i + 1, hr);
         if (hr != S_OK)
             break;
     }
-    if (FAILED(hr))
-        goto skip_source_tests;
 
     for (i = 0; i < sample_count; ++i)
     {
@@ -587,7 +584,6 @@ todo_wine
 
     get_event((IMFMediaEventGenerator *)mediasource, MEEndOfPresentation, NULL);
 
-skip_source_tests:
     IMFMediaStream_Release(video_stream);
     IMFMediaTypeHandler_Release(handler);
     IMFPresentationDescriptor_Release(descriptor);
diff --git a/dlls/winegstreamer/gst_private.h b/dlls/winegstreamer/gst_private.h
index 780cf1b02f..823e023f52 100644
--- a/dlls/winegstreamer/gst_private.h
+++ b/dlls/winegstreamer/gst_private.h
@@ -57,6 +57,7 @@ extern HRESULT mfplat_get_class_object(REFCLSID rclsid, REFIID riid, void **obj)
 
 GstCaps *make_mf_compatible_caps(GstCaps *caps);
 IMFMediaType *mf_media_type_from_caps(GstCaps *caps);
+IMFSample *mf_sample_from_gst_buffer(GstBuffer *in);
 
 enum source_type
 {
diff --git a/dlls/winegstreamer/media_source.c b/dlls/winegstreamer/media_source.c
index cb247adcef..0e53fbf424 100644
--- a/dlls/winegstreamer/media_source.c
+++ b/dlls/winegstreamer/media_source.c
@@ -56,6 +56,7 @@ struct media_stream
 enum source_async_op
 {
     SOURCE_ASYNC_START,
+    SOURCE_ASYNC_REQUEST_SAMPLE,
 };
 
 struct source_async_command
@@ -71,6 +72,11 @@ struct source_async_command
             GUID format;
             PROPVARIANT position;
         } start;
+        struct
+        {
+            struct media_stream *stream;
+            IUnknown *token;
+        } request_sample;
     } u;
 };
 
@@ -304,6 +310,76 @@ static HRESULT start_pipeline(struct media_source *source, struct source_async_c
     return S_OK;
 }
 
+static void dispatch_end_of_presentation(struct media_source *source)
+{
+    PROPVARIANT empty = {.vt = VT_EMPTY};
+
+    /* A stream has ended, check whether all have */
+    for (unsigned int i = 0; i < source->stream_count; i++)
+    {
+        struct media_stream *stream = source->streams[i];
+
+        if (stream->state != STREAM_INACTIVE && !stream->eos)
+            return;
+    }
+
+    IMFMediaEventQueue_QueueEventParamVar(source->event_queue, MEEndOfPresentation, &GUID_NULL, S_OK, &empty);
+}
+
+static HRESULT wait_on_sample(struct media_stream *stream, IUnknown *token)
+{
+    PROPVARIANT empty_var = {.vt = VT_EMPTY};
+    GstSample *gst_sample;
+    GstBuffer *buffer;
+    IMFSample *sample;
+
+    TRACE("%p, %p\n", stream, token);
+
+    g_object_get(stream->appsink, "eos", &stream->eos, NULL);
+    if (stream->eos)
+    {
+        if (token)
+            IUnknown_Release(token);
+        IMFMediaEventQueue_QueueEventParamVar(stream->event_queue, MEEndOfStream, &GUID_NULL, S_OK, &empty_var);
+        dispatch_end_of_presentation(stream->parent_source);
+        return S_OK;
+    }
+
+    g_signal_emit_by_name(stream->appsink, "pull-sample", &gst_sample);
+    assert(gst_sample);
+
+    buffer = gst_sample_get_buffer(gst_sample);
+
+    if (TRACE_ON(mfplat))
+    {
+        const GstCaps *sample_caps = gst_sample_get_caps(gst_sample);
+        const GstStructure *sample_info = gst_sample_get_info(gst_sample);
+        if (sample_caps)
+        {
+            gchar *sample_caps_str = gst_caps_to_string(sample_caps);
+            TRACE("sample caps %s\n", debugstr_a(sample_caps_str));
+            g_free(sample_caps_str);
+        }
+        if (sample_info)
+        {
+            gchar *sample_info_str = gst_structure_to_string(sample_info);
+            TRACE("sample info %s\n", debugstr_a(sample_info_str));
+            g_free(sample_info_str);
+        }
+        TRACE("PTS = %lu DTS = %lu\n", GST_BUFFER_PTS(buffer), GST_BUFFER_DTS(buffer));
+    }
+
+    sample = mf_sample_from_gst_buffer(buffer);
+    gst_sample_unref(gst_sample);
+
+    if (token)
+        IMFSample_SetUnknown(sample, &MFSampleExtension_Token, token);
+
+    IMFMediaEventQueue_QueueEventParamUnk(stream->event_queue, MEMediaSample, &GUID_NULL, S_OK, (IUnknown *)sample);
+
+    return S_OK;
+}
+
 static HRESULT WINAPI source_async_commands_Invoke(IMFAsyncCallback *iface, IMFAsyncResult *result)
 {
     struct media_source *source = impl_from_async_commands_callback_IMFAsyncCallback(iface);
@@ -323,6 +399,9 @@ static HRESULT WINAPI source_async_commands_Invoke(IMFAsyncCallback *iface, IMFA
         case SOURCE_ASYNC_START:
             start_pipeline(source, command);
             break;
+        case SOURCE_ASYNC_REQUEST_SAMPLE:
+            wait_on_sample(command->u.request_sample.stream, command->u.request_sample.token);
+            break;
         default:
             ;
     }
@@ -345,12 +424,14 @@ static const IMFAsyncCallbackVtbl source_async_commands_callback_vtbl =
 static GstFlowReturn stream_new_sample(GstElement *appsink, gpointer user)
 {
     struct media_stream *stream = (struct media_stream *) user;
-    GstSample *discard_sample;
 
-    TRACE("(%p) got sample\n", stream);
+    if (stream->state == STREAM_INACTIVE)
+    {
+        GstSample *discard_sample;
+        g_signal_emit_by_name(stream->appsink, "pull-sample", &discard_sample);
+        gst_sample_unref(discard_sample);
+    }
 
-    g_signal_emit_by_name(stream->appsink, "pull-sample", &discard_sample);
-    gst_sample_unref(discard_sample);
     return GST_FLOW_OK;
 }
 
@@ -770,13 +851,37 @@ static HRESULT WINAPI media_stream_GetStreamDescriptor(IMFMediaStream* iface, IM
 static HRESULT WINAPI media_stream_RequestSample(IMFMediaStream *iface, IUnknown *token)
 {
     struct media_stream *stream = impl_from_IMFMediaStream(iface);
+    struct source_async_command *command;
+    HRESULT hr;
 
     TRACE("(%p)->(%p)\n", iface, token);
 
     if (stream->state == STREAM_SHUTDOWN)
         return MF_E_SHUTDOWN;
 
-    return E_NOTIMPL;
+    if (stream->state == STREAM_INACTIVE || stream->state == STREAM_ENABLED)
+    {
+        WARN("Stream isn't active\n");
+        return MF_E_MEDIA_SOURCE_WRONGSTATE;
+    }
+
+    if (stream->eos)
+    {
+        return MF_E_END_OF_STREAM;
+    }
+
+    if (SUCCEEDED(hr = source_create_async_op(SOURCE_ASYNC_REQUEST_SAMPLE, &command)))
+    {
+        command->u.request_sample.stream = stream;
+        if (token)
+            IUnknown_AddRef(token);
+        command->u.request_sample.token = token;
+
+        /* Once pause support is added, this will need to into a stream queue, and synchronization will need to be added*/
+        hr = MFPutWorkItem(stream->parent_source->async_commands_queue, &stream->parent_source->async_commands_callback, &command->IUnknown_iface);
+    }
+
+    return hr;
 }
 
 static const IMFMediaStreamVtbl media_stream_vtbl =
@@ -898,6 +1003,7 @@ static HRESULT media_stream_constructor(struct media_source *source, GstPad *pad
     object->their_src = pad;
 
     object->state = STREAM_INACTIVE;
+    object->eos = FALSE;
 
     if (FAILED(hr = MFCreateEventQueue(&object->event_queue)))
         goto fail;
diff --git a/dlls/winegstreamer/mfplat.c b/dlls/winegstreamer/mfplat.c
index c74f956339..81782eeab3 100644
--- a/dlls/winegstreamer/mfplat.c
+++ b/dlls/winegstreamer/mfplat.c
@@ -871,4 +871,94 @@ GstCaps *make_mf_compatible_caps(GstCaps *caps)
     }
 
     return ret;
+}
+
+/* IMFSample = GstBuffer
+   IMFBuffer = GstMemory */
+
+/* TODO: Future optimization could be to create a custom
+   IMFMediaBuffer wrapper around GstMemory, and to utilize
+   gst_memory_new_wrapped on IMFMediaBuffer data.  However,
+   this wouldn't work if we allow the callers to allocate
+   the buffers. */
+
+IMFSample* mf_sample_from_gst_buffer(GstBuffer *gst_buffer)
+{
+    IMFSample *out = NULL;
+    LONGLONG duration, time;
+    int buffer_count;
+    HRESULT hr;
+
+    if (FAILED(hr = MFCreateSample(&out)))
+        goto fail;
+
+    duration = GST_BUFFER_DURATION(gst_buffer);
+    time = GST_BUFFER_PTS(gst_buffer);
+
+    if (FAILED(IMFSample_SetSampleDuration(out, duration / 100)))
+        goto fail;
+
+    if (FAILED(IMFSample_SetSampleTime(out, time / 100)))
+        goto fail;
+
+    buffer_count = gst_buffer_n_memory(gst_buffer);
+
+    for (unsigned int i = 0; i < buffer_count; i++)
+    {
+        GstMemory *memory = gst_buffer_get_memory(gst_buffer, i);
+        IMFMediaBuffer *mf_buffer = NULL;
+        GstMapInfo map_info;
+        BYTE *buf_data;
+
+        if (!memory)
+        {
+            hr = E_FAIL;
+            goto loop_done;
+        }
+
+        if (!(gst_memory_map(memory, &map_info, GST_MAP_READ)))
+        {
+            hr = E_FAIL;
+            goto loop_done;
+        }
+
+        if (FAILED(hr = MFCreateMemoryBuffer(map_info.maxsize, &mf_buffer)))
+        {
+            gst_memory_unmap(memory, &map_info);
+            goto loop_done;
+        }
+
+        if (FAILED(hr = IMFMediaBuffer_Lock(mf_buffer, &buf_data, NULL, NULL)))
+        {
+            gst_memory_unmap(memory, &map_info);
+            goto loop_done;
+        }
+
+        memcpy(buf_data, map_info.data, map_info.size);
+
+        gst_memory_unmap(memory, &map_info);
+
+        if (FAILED(hr = IMFMediaBuffer_Unlock(mf_buffer)))
+            goto loop_done;
+
+        if (FAILED(hr = IMFMediaBuffer_SetCurrentLength(mf_buffer, map_info.size)))
+            goto loop_done;
+
+        if (FAILED(hr = IMFSample_AddBuffer(out, mf_buffer)))
+            goto loop_done;
+
+        loop_done:
+        if (mf_buffer)
+            IMFMediaBuffer_Release(mf_buffer);
+        if (memory)
+            gst_memory_unref(memory);
+        if (FAILED(hr))
+            goto fail;
+    }
+
+    return out;
+    fail:
+    ERR("Failed to copy IMFSample to GstBuffer, hr = %#x\n", hr);
+    IMFSample_Release(out);
+    return NULL;
 }
\ No newline at end of file
-- 
2.25.3

From 0d76e75118c3020e6d64c17016563d36f8928f5b Mon Sep 17 00:00:00 2001
From: Derek Lesho <dlesho@codeweavers.com>
Date: Tue, 31 Mar 2020 15:15:07 -0500
Subject: [PATCH 19/46] winegstreamer: Implement
 IMFMediaSource::GetCharacteristics.

Signed-off-by: Derek Lesho <dlesho@codeweavers.com>
---
 dlls/winegstreamer/media_source.c | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/dlls/winegstreamer/media_source.c b/dlls/winegstreamer/media_source.c
index 0e53fbf424..78865259e6 100644
--- a/dlls/winegstreamer/media_source.c
+++ b/dlls/winegstreamer/media_source.c
@@ -1159,12 +1159,14 @@ static HRESULT WINAPI media_source_GetCharacteristics(IMFMediaSource *iface, DWO
 {
     struct media_source *source = impl_from_IMFMediaSource(iface);
 
-    FIXME("(%p)->(%p): stub\n", source, characteristics);
+    TRACE("(%p)->(%p)\n", source, characteristics);
 
     if (source->state == SOURCE_SHUTDOWN)
         return MF_E_SHUTDOWN;
 
-    return E_NOTIMPL;
+    *characteristics = MFMEDIASOURCE_CAN_SEEK;
+
+    return S_OK;
 }
 
 static HRESULT WINAPI media_source_CreatePresentationDescriptor(IMFMediaSource *iface, IMFPresentationDescriptor **descriptor)
-- 
2.25.3

From 1d7fe6489f18bf77f6d81e33e239939bd0ddacb1 Mon Sep 17 00:00:00 2001
From: Derek Lesho <dlesho@codeweavers.com>
Date: Tue, 31 Mar 2020 15:15:50 -0500
Subject: [PATCH 20/46] winegstreamer: Calculate the MF_PD_DURATION of the
 media source's PD.

Signed-off-by: Derek Lesho <dlesho@codeweavers.com>
---
 dlls/winegstreamer/media_source.c | 40 +++++++++++++++++++++++++++++++
 1 file changed, 40 insertions(+)

diff --git a/dlls/winegstreamer/media_source.c b/dlls/winegstreamer/media_source.c
index 78865259e6..b88f794d86 100644
--- a/dlls/winegstreamer/media_source.c
+++ b/dlls/winegstreamer/media_source.c
@@ -1422,6 +1422,46 @@ static HRESULT media_source_constructor(IMFByteStream *bytestream, enum source_t
     heap_free(descriptors);
     descriptors = NULL;
 
+    /* miscelaneous presentation descriptor setup */
+    {
+        IMFAttributes *byte_stream_attributes;
+        gint64 total_pres_time = 0;
+
+        if (SUCCEEDED(IMFByteStream_QueryInterface(object->byte_stream, &IID_IMFAttributes, (void **)&byte_stream_attributes)))
+        {
+            WCHAR *mimeW = NULL;
+            DWORD length;
+            if (SUCCEEDED(IMFAttributes_GetAllocatedString(byte_stream_attributes, &MF_BYTESTREAM_CONTENT_TYPE, &mimeW, &length)))
+            {
+                IMFPresentationDescriptor_SetString(object->pres_desc, &MF_PD_MIME_TYPE, mimeW);
+                CoTaskMemFree(mimeW);
+            }
+            IMFAttributes_Release(byte_stream_attributes);
+        }
+
+        for (unsigned int i = 0; i < object->stream_count; i++)
+        {
+            GstQuery *query = gst_query_new_duration(GST_FORMAT_TIME);
+            if (gst_pad_query(object->streams[i]->their_src, query))
+            {
+                gint64 stream_pres_time;
+                gst_query_parse_duration(query, NULL, &stream_pres_time);
+
+                TRACE("Stream %u has duration %lu\n", i, stream_pres_time);
+
+                if (stream_pres_time > total_pres_time)
+                    total_pres_time = stream_pres_time;
+            }
+            else
+            {
+                WARN("Unable to get presentation time of stream %u\n", i);
+            }
+        }
+
+        if (object->stream_count)
+            IMFPresentationDescriptor_SetUINT64(object->pres_desc, &MF_PD_DURATION, total_pres_time / 100);
+    }
+
     gst_element_set_state(object->container, GST_STATE_READY);
 
     object->state = SOURCE_STOPPED;
-- 
2.25.3

From f8624e25e0caff8486170304b5a4c35c21c9439e Mon Sep 17 00:00:00 2001
From: Derek Lesho <dlesho@codeweavers.com>
Date: Wed, 29 Jan 2020 15:37:39 -0600
Subject: [PATCH 21/46] tools: Add support for multiple parent directories.

Signed-off-by: Derek Lesho <dlesho@codeweavers.com>
---
 tools/make_makefiles | 45 +++++++++++++++++++++++++++-----------------
 tools/makedep.c      | 30 ++++++++++++++++++++---------
 2 files changed, 49 insertions(+), 26 deletions(-)

diff --git a/tools/make_makefiles b/tools/make_makefiles
index c18fa90e2d..1e34a280a4 100755
--- a/tools/make_makefiles
+++ b/tools/make_makefiles
@@ -231,14 +231,14 @@ sub parse_makefile($)
         {
             die "Configure substitution is not allowed in $file" unless $file eq "Makefile";
         }
-        if (/^\s*(MODULE|IMPORTLIB|TESTDLL|PARENTSRC|APPMODE|EXTRADLLFLAGS)\s*=\s*(.*)/)
+        if (/^\s*(MODULE|IMPORTLIB|TESTDLL|APPMODE|EXTRADLLFLAGS)\s*=\s*(.*)/)
         {
             my $var = $1;
             $make{$var} = $2;
             next;
         }
         my $source_vars_regexp = join "|", @source_vars;
-        if (/^\s*($source_vars_regexp|PROGRAMS|EXTRA_TARGETS|EXTRA_OBJS|INSTALL_LIB|INSTALL_DEV)\s*=\s*(.*)/)
+        if (/^\s*($source_vars_regexp|PROGRAMS|EXTRA_TARGETS|EXTRA_OBJS|INSTALL_LIB|INSTALL_DEV|PARENTSRC)\s*=\s*(.*)/)
         {
             my $var = $1;
             my @list = split(/\s+/, $2);
@@ -293,19 +293,27 @@ sub get_makedep_flags($)
     return %flags;
 }
 
-sub get_parent_makefile($)
+sub get_parent_makefiles($)
 {
     my $file = shift;
     my %make = %{$makefiles{$file}};
-    my $reldir = $make{"PARENTSRC"} || "";
-    return "" unless $reldir;
-    (my $path = $file) =~ s/\/Makefile$/\//;
-    while ($reldir =~ /^\.\.\//)
+    my $pointer = $make{"PARENTSRC"} || ();
+    return () unless $pointer;
+    my @reldirs = @{$pointer};
+    my @makefiles = ();
+    foreach my $reldir (@reldirs)
     {
-        $reldir =~ s/^\.\.\///;
-        $path =~ s/[^\/]+\/$//;
+        my $length = @reldirs;
+        (my $path = $file) =~ s/\/Makefile$/\//;
+        while ($reldir =~ /^\.\.\//)
+        {
+            $reldir =~ s/^\.\.\///;
+            $path =~ s/[^\/]+\/$//;
+        }
+        push @makefiles, "$path$reldir/Makefile";
     }
-    return "$path$reldir/Makefile";
+
+    return @makefiles
 }
 
 # preserve shared source files that are listed in the existing makefile
@@ -410,13 +418,16 @@ sub assign_sources_to_makefiles(@)
     foreach my $file (@makefiles)
     {
         my $make = $makefiles{$file};
-        my $parent = get_parent_makefile( $file );
-        next unless $parent;
-        preserve_shared_source_files( $makefiles{$file}, $makefiles{$parent}, "C_SRCS" );
-        preserve_shared_source_files( $makefiles{$file}, $makefiles{$parent}, "RC_SRCS" );
-        preserve_shared_source_files( $makefiles{$file}, $makefiles{$parent}, "IDL_SRCS" );
-        preserve_shared_source_files( $makefiles{$file}, $makefiles{$parent}, "LEX_SRCS" );
-        preserve_shared_source_files( $makefiles{$file}, $makefiles{$parent}, "BISON_SRCS" );
+        my @parents = get_parent_makefiles( $file );
+        next unless @parents;
+        foreach my $parent (@parents)
+        {
+            preserve_shared_source_files( $makefiles{$file}, $makefiles{$parent}, "C_SRCS" );
+            preserve_shared_source_files( $makefiles{$file}, $makefiles{$parent}, "RC_SRCS" );
+            preserve_shared_source_files( $makefiles{$file}, $makefiles{$parent}, "IDL_SRCS" );
+            preserve_shared_source_files( $makefiles{$file}, $makefiles{$parent}, "LEX_SRCS" );
+            preserve_shared_source_files( $makefiles{$file}, $makefiles{$parent}, "BISON_SRCS" );
+        }
     }
 }
 
diff --git a/tools/makedep.c b/tools/makedep.c
index fb2e145ced..c505d26f43 100644
--- a/tools/makedep.c
+++ b/tools/makedep.c
@@ -182,6 +182,7 @@ struct makefile
     struct strarray install_lib;
     struct strarray install_dev;
     struct strarray extra_targets;
+    struct strarray parent_dirs;
     struct list     sources;
     struct list     includes;
     const char     *base_dir;
@@ -189,7 +190,6 @@ struct makefile
     const char     *obj_dir;
     const char     *top_src_dir;
     const char     *top_obj_dir;
-    const char     *parent_dir;
     const char     *module;
     const char     *testdll;
     const char     *sharedlib;
@@ -1373,15 +1373,24 @@ static struct file *open_local_file( const struct makefile *make, const char *pa
 {
     char *src_path = root_dir_path( base_dir_path( make, path ));
     struct file *ret = load_file( src_path );
+    unsigned int i;
 
-    /* if not found, try parent dir */
-    if (!ret && make->parent_dir)
+    /* if not found, try parent dirs */
+    for (i = 0; !ret && i < make->parent_dirs.count; i++)
     {
+        char *new_path;
+
         free( src_path );
-        path = strmake( "%s/%s", make->parent_dir, path );
-        src_path = root_dir_path( base_dir_path( make, path ));
+        new_path = strmake( "%s/%s", make->parent_dirs.str[i], path );
+        src_path = root_dir_path( base_dir_path( make, new_path ));
         ret = load_file( src_path );
-        if (ret) ret->flags |= FLAG_PARENTDIR;
+        if (ret)
+        {
+            ret->flags |= FLAG_PARENTDIR;
+            path = new_path;
+        }
+        else
+            free(new_path);
     }
 
     if (ret) *filename = src_dir_path( make, path );
@@ -4189,13 +4198,13 @@ static void load_sources( struct makefile *make )
     strarray_set_value( &make->vars, "top_srcdir", top_src_dir_path( make, "" ));
     strarray_set_value( &make->vars, "srcdir", src_dir_path( make, "" ));
 
-    make->parent_dir    = get_expanded_make_variable( make, "PARENTSRC" );
     make->module        = get_expanded_make_variable( make, "MODULE" );
     make->testdll       = get_expanded_make_variable( make, "TESTDLL" );
     make->sharedlib     = get_expanded_make_variable( make, "SHAREDLIB" );
     make->staticlib     = get_expanded_make_variable( make, "STATICLIB" );
     make->importlib     = get_expanded_make_variable( make, "IMPORTLIB" );
 
+    make->parent_dirs   = get_expanded_make_var_array( make, "PARENTSRC" );
     make->programs      = get_expanded_make_var_array( make, "PROGRAMS" );
     make->scripts       = get_expanded_make_var_array( make, "SCRIPTS" );
     make->imports       = get_expanded_make_var_array( make, "IMPORTS" );
@@ -4240,8 +4249,11 @@ static void load_sources( struct makefile *make )
     strarray_add( &make->include_args, strmake( "-I%s", obj_dir_path( make, "" )));
     if (make->src_dir)
         strarray_add( &make->include_args, strmake( "-I%s", make->src_dir ));
-    if (make->parent_dir)
-        strarray_add( &make->include_args, strmake( "-I%s", src_dir_path( make, make->parent_dir )));
+    if (make->parent_dirs.count)
+    {
+        for (i = 0; i < make->parent_dirs.count; i++)
+            strarray_add( &make->include_args, strmake( "-I%s", src_dir_path( make, make->parent_dirs.str[i] )));
+    }
     strarray_add( &make->include_args, strmake( "-I%s", top_obj_dir_path( make, "include" )));
     if (make->top_src_dir)
         strarray_add( &make->include_args, strmake( "-I%s", top_src_dir_path( make, "include" )));
-- 
2.25.3

From b42166858eb03db474fc40f9ead7e9c7a06dddae Mon Sep 17 00:00:00 2001
From: Derek Lesho <dlesho@codeweavers.com>
Date: Wed, 29 Jan 2020 15:30:49 -0600
Subject: [PATCH 22/46] mf: Introduce handler helper.

Signed-off-by: Derek Lesho <dlesho@codeweavers.com>
---
 dlls/mf/Makefile.in               |   1 +
 dlls/mf/handler.c                 | 360 ++++++++++++++++++++++++++++
 dlls/mf/handler.h                 |  30 +++
 dlls/mf/main.c                    | 373 ++++--------------------------
 dlls/winegstreamer/Makefile.in    |   3 +-
 dlls/winegstreamer/media_source.c | 335 ++-------------------------
 6 files changed, 447 insertions(+), 655 deletions(-)
 create mode 100644 dlls/mf/handler.c
 create mode 100644 dlls/mf/handler.h

diff --git a/dlls/mf/Makefile.in b/dlls/mf/Makefile.in
index fe156e43ab..5c69ac38e5 100644
--- a/dlls/mf/Makefile.in
+++ b/dlls/mf/Makefile.in
@@ -5,6 +5,7 @@ IMPORTS   = advapi32 mfplat ole32 uuid mfuuid
 EXTRADLLFLAGS = -mno-cygwin
 
 C_SRCS = \
+	handler.c \
 	main.c \
 	samplegrabber.c \
 	sar.c \
diff --git a/dlls/mf/handler.c b/dlls/mf/handler.c
new file mode 100644
index 0000000000..da40049377
--- /dev/null
+++ b/dlls/mf/handler.c
@@ -0,0 +1,360 @@
+#include <stdarg.h>
+
+#define COBJMACROS
+
+#include "windef.h"
+#include "winbase.h"
+#include "mfidl.h"
+
+#include "mfapi.h"
+#include "mferror.h"
+
+#include "handler.h"
+
+#include "wine/debug.h"
+#include "wine/heap.h"
+#include "wine/list.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(mfplat);
+
+struct create_object_context
+{
+    IUnknown IUnknown_iface;
+    LONG refcount;
+
+    IPropertyStore *props;
+    IMFByteStream *stream;
+    WCHAR *url;
+    DWORD flags;
+};
+
+struct handler_result
+{
+    struct list entry;
+    IMFAsyncResult *result;
+    MF_OBJECT_TYPE obj_type;
+    IUnknown *object;
+};
+
+static struct create_object_context *impl_from_IUnknown(IUnknown *iface)
+{
+    return CONTAINING_RECORD(iface, struct create_object_context, IUnknown_iface);
+}
+
+static HRESULT WINAPI create_object_context_QueryInterface(IUnknown *iface, REFIID riid, void **obj)
+{
+    TRACE("%p, %s, %p.\n", iface, debugstr_guid(riid), obj);
+
+    if (IsEqualIID(riid, &IID_IUnknown))
+    {
+        *obj = iface;
+        IUnknown_AddRef(iface);
+        return S_OK;
+    }
+
+    WARN("Unsupported %s.\n", debugstr_guid(riid));
+    *obj = NULL;
+    return E_NOINTERFACE;
+}
+
+static ULONG WINAPI create_object_context_AddRef(IUnknown *iface)
+{
+    struct create_object_context *context = impl_from_IUnknown(iface);
+    ULONG refcount = InterlockedIncrement(&context->refcount);
+
+    TRACE("%p, refcount %u.\n", iface, refcount);
+
+    return refcount;
+}
+
+static ULONG WINAPI create_object_context_Release(IUnknown *iface)
+{
+    struct create_object_context *context = impl_from_IUnknown(iface);
+    ULONG refcount = InterlockedDecrement(&context->refcount);
+
+    TRACE("%p, refcount %u.\n", iface, refcount);
+
+    if (!refcount)
+    {
+        if (context->props)
+            IPropertyStore_Release(context->props);
+        if (context->stream)
+            IMFByteStream_Release(context->stream);
+        if (context->url)
+            heap_free(context->url);
+        heap_free(context);
+    }
+
+    return refcount;
+}
+
+static const IUnknownVtbl create_object_context_vtbl =
+{
+    create_object_context_QueryInterface,
+    create_object_context_AddRef,
+    create_object_context_Release,
+};
+
+/* Start async methods */
+static struct handler *impl_from_IMFAsyncCallback(IMFAsyncCallback *iface)
+{
+    return CONTAINING_RECORD(iface, struct handler, IMFAsyncCallback_iface);
+}
+
+static HRESULT WINAPI handler_callback_QueryInterface(IMFAsyncCallback *iface, REFIID riid, void **obj)
+{
+    if (IsEqualIID(riid, &IID_IMFAsyncCallback) ||
+            IsEqualIID(riid, &IID_IUnknown))
+    {
+        *obj = iface;
+        IMFAsyncCallback_AddRef(iface);
+        return S_OK;
+    }
+
+    WARN("Unsupported %s.\n", debugstr_guid(riid));
+    *obj = NULL;
+    return E_NOINTERFACE;
+}
+
+/* lifetime not managed with COM */
+static ULONG WINAPI handler_callback_AddRef(IMFAsyncCallback *iface)
+{
+    return 2;
+}
+
+static ULONG WINAPI handler_callback_Release(IMFAsyncCallback *iface)
+{
+    return 1;
+}
+
+static HRESULT WINAPI handler_callback_GetParameters(IMFAsyncCallback *iface, DWORD *flags, DWORD *queue)
+{
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI handler_callback_Invoke(IMFAsyncCallback *iface, IMFAsyncResult *result)
+{
+    struct handler *handler = impl_from_IMFAsyncCallback(iface);
+    struct handler_result *handler_result;
+    MF_OBJECT_TYPE obj_type = MF_OBJECT_INVALID;
+    IUnknown *object = NULL, *context_object;
+    struct create_object_context *context;
+    IMFAsyncResult *caller;
+    HRESULT hr;
+
+    caller = (IMFAsyncResult *)IMFAsyncResult_GetStateNoAddRef(result);
+
+    if (FAILED(hr = IMFAsyncResult_GetObject(result, &context_object)))
+    {
+        WARN("Expected context set for callee result.\n");
+        return hr;
+    }
+
+    context = impl_from_IUnknown(context_object);
+
+    hr = handler->create_object(handler, context->url, context->stream, context->flags, context->props, &object, &obj_type);
+
+    handler_result = heap_alloc(sizeof(*handler_result));
+    if (handler_result)
+    {
+        handler_result->result = caller;
+        IMFAsyncResult_AddRef(handler_result->result);
+        handler_result->obj_type = obj_type;
+        handler_result->object = object;
+
+        EnterCriticalSection(&handler->cs);
+        list_add_tail(&handler->results, &handler_result->entry);
+        LeaveCriticalSection(&handler->cs);
+    }
+    else
+    {
+        if (object)
+            IUnknown_Release(object);
+        hr = E_OUTOFMEMORY;
+    }
+
+    IUnknown_Release(&context->IUnknown_iface);
+
+    IMFAsyncResult_SetStatus(caller, hr);
+    MFInvokeCallback(caller);
+
+    return S_OK;
+}
+
+static const IMFAsyncCallbackVtbl handler_callback_vtbl =
+{
+    handler_callback_QueryInterface,
+    handler_callback_AddRef,
+    handler_callback_Release,
+    handler_callback_GetParameters,
+    handler_callback_Invoke,
+};
+
+/* Start handler helpers */
+
+static WCHAR *heap_strdupW(const WCHAR *str)
+{
+    WCHAR *ret = NULL;
+
+    if (str)
+    {
+        unsigned int size;
+
+        size = (lstrlenW(str) + 1) * sizeof(WCHAR);
+        ret = heap_alloc(size);
+        if (ret)
+            memcpy(ret, str, size);
+    }
+
+    return ret;
+}
+
+HRESULT handler_begin_create_object(struct handler *handler, IMFByteStream *stream,
+        const WCHAR *url, DWORD flags, IPropertyStore *props, IUnknown **cancel_cookie,
+        IMFAsyncCallback *callback, IUnknown *state)
+{
+    struct create_object_context *context;
+    IMFAsyncResult *caller, *item;
+    HRESULT hr;
+
+    if (cancel_cookie)
+        *cancel_cookie = NULL;
+
+    if (FAILED(hr = MFCreateAsyncResult(NULL, callback, state, &caller)))
+        return hr;
+
+    context = heap_alloc(sizeof(*context));
+    if (!context)
+    {
+        IMFAsyncResult_Release(caller);
+        return E_OUTOFMEMORY;
+    }
+
+    context->IUnknown_iface.lpVtbl = &create_object_context_vtbl;
+    context->refcount = 1;
+    context->props = props;
+    if (context->props)
+        IPropertyStore_AddRef(context->props);
+    context->flags = flags;
+    context->stream = stream;
+    if (context->stream)
+        IMFByteStream_AddRef(context->stream);
+    if (url)
+        context->url = heap_strdupW(url);
+    if (!context->url && !context->stream)
+    {
+        IMFAsyncResult_Release(caller);
+        IUnknown_Release(&context->IUnknown_iface);
+        return E_OUTOFMEMORY;
+    }
+
+    hr = MFCreateAsyncResult(&context->IUnknown_iface, &handler->IMFAsyncCallback_iface, (IUnknown *)caller, &item);
+    IUnknown_Release(&context->IUnknown_iface);
+    if (SUCCEEDED(hr))
+    {
+        if (SUCCEEDED(hr = MFPutWorkItemEx(MFASYNC_CALLBACK_QUEUE_IO, item)))
+        {
+            if (cancel_cookie)
+            {
+                *cancel_cookie = (IUnknown *)caller;
+                IUnknown_AddRef(*cancel_cookie);
+            }
+        }
+
+        IMFAsyncResult_Release(item);
+    }
+    IMFAsyncResult_Release(caller);
+
+    return hr;
+}
+
+HRESULT handler_end_create_object(struct handler *handler, IMFAsyncResult *result,
+        MF_OBJECT_TYPE *obj_type, IUnknown **object)
+{
+    struct handler_result *found = NULL, *cur;
+    HRESULT hr;
+
+    EnterCriticalSection(&handler->cs);
+
+    LIST_FOR_EACH_ENTRY(cur, &handler->results, struct handler_result, entry)
+    {
+        if (result == cur->result)
+        {
+            list_remove(&cur->entry);
+            found = cur;
+            break;
+        }
+    }
+
+    LeaveCriticalSection(&handler->cs);
+
+    if (found)
+    {
+        *obj_type = found->obj_type;
+        *object = found->object;
+        hr = IMFAsyncResult_GetStatus(found->result);
+        IMFAsyncResult_Release(found->result);
+        heap_free(found);
+    }
+    else
+    {
+        *obj_type = MF_OBJECT_INVALID;
+        *object = NULL;
+        hr = MF_E_UNEXPECTED;
+    }
+
+    return hr;
+}
+
+HRESULT handler_cancel_object_creation(struct handler *handler, IUnknown *cancel_cookie)
+{
+    struct handler_result *found = NULL, *cur;
+
+    EnterCriticalSection(&handler->cs);
+
+    LIST_FOR_EACH_ENTRY(cur, &handler->results, struct handler_result, entry)
+    {
+        if (cancel_cookie == (IUnknown *)cur->result)
+        {
+            list_remove(&cur->entry);
+            found = cur;
+            break;
+        }
+    }
+
+    LeaveCriticalSection(&handler->cs);
+
+    if (found)
+    {
+        IMFAsyncResult_Release(found->result);
+        if (found->object)
+            IUnknown_Release(found->object);
+        heap_free(found);
+    }
+
+    return found ? S_OK : MF_E_UNEXPECTED;
+}
+
+void handler_construct(struct handler *handler, p_create_object_callback create_object_callback)
+{
+    handler->IMFAsyncCallback_iface.lpVtbl = &handler_callback_vtbl;
+    handler->create_object = create_object_callback;
+
+    list_init(&handler->results);
+    InitializeCriticalSection(&handler->cs);
+}
+
+void handler_destruct(struct handler *handler)
+{
+    struct handler_result *result, *next;
+
+    LIST_FOR_EACH_ENTRY_SAFE(result, next, &handler->results, struct handler_result, entry)
+    {
+        list_remove(&result->entry);
+        IMFAsyncResult_Release(result->result);
+        if (result->object)
+            IUnknown_Release(result->object);
+        heap_free(result);
+    }
+    DeleteCriticalSection(&handler->cs);
+}
\ No newline at end of file
diff --git a/dlls/mf/handler.h b/dlls/mf/handler.h
new file mode 100644
index 0000000000..fbebd26e8d
--- /dev/null
+++ b/dlls/mf/handler.h
@@ -0,0 +1,30 @@
+#include "windef.h"
+
+#include "mfidl.h"
+#include "mfapi.h"
+#include "mfobjects.h"
+
+#include "wine/list.h"
+
+/* helper sub-object that handles ansyncronous nature of handlers */
+
+struct handler;
+typedef HRESULT (*p_create_object_callback)(struct handler *handler, WCHAR *url, IMFByteStream *stream, DWORD flags, IPropertyStore *props,
+                                            IUnknown **out_object, MF_OBJECT_TYPE *out_obj_type);
+
+struct handler
+{
+    IMFAsyncCallback IMFAsyncCallback_iface;
+    struct list results;
+    CRITICAL_SECTION cs;
+    p_create_object_callback create_object;
+};
+
+void handler_construct(struct handler *handler, p_create_object_callback create_object_callback);
+void handler_destruct(struct handler *handler);
+HRESULT handler_begin_create_object(struct handler *handler, IMFByteStream *stream,
+        const WCHAR *url, DWORD flags, IPropertyStore *props, IUnknown **cancel_cookie,
+        IMFAsyncCallback *callback, IUnknown *state);
+HRESULT handler_end_create_object(struct handler *handler, IMFAsyncResult *result,
+        MF_OBJECT_TYPE *obj_type, IUnknown **object);
+HRESULT handler_cancel_object_creation(struct handler *handler, IUnknown *cancel_cookie);
\ No newline at end of file
diff --git a/dlls/mf/main.c b/dlls/mf/main.c
index 15c9e5e3c9..fe50dbac5a 100644
--- a/dlls/mf/main.c
+++ b/dlls/mf/main.c
@@ -30,6 +30,7 @@
 #include "mferror.h"
 
 #include "mf_private.h"
+#include "handler.h"
 
 #include "wine/debug.h"
 #include "wine/heap.h"
@@ -552,22 +553,12 @@ static const IClassFactoryVtbl class_factory_vtbl =
     class_factory_LockServer,
 };
 
-struct file_scheme_handler_result
-{
-    struct list entry;
-    IMFAsyncResult *result;
-    MF_OBJECT_TYPE obj_type;
-    IUnknown *object;
-};
-
 struct file_scheme_handler
 {
     IMFSchemeHandler IMFSchemeHandler_iface;
-    IMFAsyncCallback IMFAsyncCallback_iface;
     LONG refcount;
     IMFSourceResolver *resolver;
-    struct list results;
-    CRITICAL_SECTION cs;
+    struct handler handler;
 };
 
 static struct file_scheme_handler *impl_from_IMFSchemeHandler(IMFSchemeHandler *iface)
@@ -575,11 +566,6 @@ static struct file_scheme_handler *impl_from_IMFSchemeHandler(IMFSchemeHandler *
     return CONTAINING_RECORD(iface, struct file_scheme_handler, IMFSchemeHandler_iface);
 }
 
-static struct file_scheme_handler *impl_from_IMFAsyncCallback(IMFAsyncCallback *iface)
-{
-    return CONTAINING_RECORD(iface, struct file_scheme_handler, IMFAsyncCallback_iface);
-}
-
 static HRESULT WINAPI file_scheme_handler_QueryInterface(IMFSchemeHandler *iface, REFIID riid, void **obj)
 {
     TRACE("%p, %s, %p.\n", iface, debugstr_guid(riid), obj);
@@ -609,242 +595,45 @@ static ULONG WINAPI file_scheme_handler_AddRef(IMFSchemeHandler *iface)
 
 static ULONG WINAPI file_scheme_handler_Release(IMFSchemeHandler *iface)
 {
-    struct file_scheme_handler *handler = impl_from_IMFSchemeHandler(iface);
-    ULONG refcount = InterlockedDecrement(&handler->refcount);
-    struct file_scheme_handler_result *result, *next;
+    struct file_scheme_handler *this = impl_from_IMFSchemeHandler(iface);
+    ULONG refcount = InterlockedDecrement(&this->refcount);
 
     TRACE("%p, refcount %u.\n", iface, refcount);
 
     if (!refcount)
     {
-        LIST_FOR_EACH_ENTRY_SAFE(result, next, &handler->results, struct file_scheme_handler_result, entry)
-        {
-            list_remove(&result->entry);
-            IMFAsyncResult_Release(result->result);
-            if (result->object)
-                IUnknown_Release(result->object);
-            heap_free(result);
-        }
-        DeleteCriticalSection(&handler->cs);
-        if (handler->resolver)
-            IMFSourceResolver_Release(handler->resolver);
-        heap_free(handler);
+        if (this->resolver)
+            IMFSourceResolver_Release(this->resolver);
+        handler_destruct(&this->handler);
     }
 
     return refcount;
 }
 
-struct create_object_context
-{
-    IUnknown IUnknown_iface;
-    LONG refcount;
-
-    IPropertyStore *props;
-    WCHAR *url;
-    DWORD flags;
-};
-
-static struct create_object_context *impl_from_IUnknown(IUnknown *iface)
-{
-    return CONTAINING_RECORD(iface, struct create_object_context, IUnknown_iface);
-}
-
-static HRESULT WINAPI create_object_context_QueryInterface(IUnknown *iface, REFIID riid, void **obj)
-{
-    TRACE("%p, %s, %p.\n", iface, debugstr_guid(riid), obj);
-
-    if (IsEqualIID(riid, &IID_IUnknown))
-    {
-        *obj = iface;
-        IUnknown_AddRef(iface);
-        return S_OK;
-    }
-
-    WARN("Unsupported %s.\n", debugstr_guid(riid));
-    *obj = NULL;
-    return E_NOINTERFACE;
-}
-
-static ULONG WINAPI create_object_context_AddRef(IUnknown *iface)
-{
-    struct create_object_context *context = impl_from_IUnknown(iface);
-    ULONG refcount = InterlockedIncrement(&context->refcount);
-
-    TRACE("%p, refcount %u.\n", iface, refcount);
-
-    return refcount;
-}
-
-static ULONG WINAPI create_object_context_Release(IUnknown *iface)
-{
-    struct create_object_context *context = impl_from_IUnknown(iface);
-    ULONG refcount = InterlockedDecrement(&context->refcount);
-
-    TRACE("%p, refcount %u.\n", iface, refcount);
-
-    if (!refcount)
-    {
-        if (context->props)
-            IPropertyStore_Release(context->props);
-        heap_free(context->url);
-        heap_free(context);
-    }
-
-    return refcount;
-}
-
-static const IUnknownVtbl create_object_context_vtbl =
-{
-    create_object_context_QueryInterface,
-    create_object_context_AddRef,
-    create_object_context_Release,
-};
-
-static WCHAR *heap_strdupW(const WCHAR *str)
-{
-    WCHAR *ret = NULL;
-
-    if (str)
-    {
-        unsigned int size;
-
-        size = (lstrlenW(str) + 1) * sizeof(WCHAR);
-        ret = heap_alloc(size);
-        if (ret)
-            memcpy(ret, str, size);
-    }
-
-    return ret;
-}
-
 static HRESULT WINAPI file_scheme_handler_BeginCreateObject(IMFSchemeHandler *iface, const WCHAR *url, DWORD flags,
         IPropertyStore *props, IUnknown **cancel_cookie, IMFAsyncCallback *callback, IUnknown *state)
 {
-    struct file_scheme_handler *handler = impl_from_IMFSchemeHandler(iface);
-    struct create_object_context *context;
-    IMFAsyncResult *caller, *item;
-    HRESULT hr;
+    struct file_scheme_handler *this = impl_from_IMFSchemeHandler(iface);
 
     TRACE("%p, %s, %#x, %p, %p, %p, %p.\n", iface, debugstr_w(url), flags, props, cancel_cookie, callback, state);
-
-    if (cancel_cookie)
-        *cancel_cookie = NULL;
-
-    if (FAILED(hr = MFCreateAsyncResult(NULL, callback, state, &caller)))
-        return hr;
-
-    context = heap_alloc(sizeof(*context));
-    if (!context)
-    {
-        IMFAsyncResult_Release(caller);
-        return E_OUTOFMEMORY;
-    }
-
-    context->IUnknown_iface.lpVtbl = &create_object_context_vtbl;
-    context->refcount = 1;
-    context->props = props;
-    if (context->props)
-        IPropertyStore_AddRef(context->props);
-    context->flags = flags;
-    context->url = heap_strdupW(url);
-    if (!context->url)
-    {
-        IMFAsyncResult_Release(caller);
-        IUnknown_Release(&context->IUnknown_iface);
-        return E_OUTOFMEMORY;
-    }
-
-    hr = MFCreateAsyncResult(&context->IUnknown_iface, &handler->IMFAsyncCallback_iface, (IUnknown *)caller, &item);
-    IUnknown_Release(&context->IUnknown_iface);
-    if (SUCCEEDED(hr))
-    {
-        if (SUCCEEDED(hr = MFPutWorkItemEx(MFASYNC_CALLBACK_QUEUE_IO, item)))
-        {
-            if (cancel_cookie)
-            {
-                *cancel_cookie = (IUnknown *)caller;
-                IUnknown_AddRef(*cancel_cookie);
-            }
-        }
-
-        IMFAsyncResult_Release(item);
-    }
-    IMFAsyncResult_Release(caller);
-
-    return hr;
+    return handler_begin_create_object(&this->handler, NULL, url, flags, props, cancel_cookie, callback, state);
 }
 
 static HRESULT WINAPI file_scheme_handler_EndCreateObject(IMFSchemeHandler *iface, IMFAsyncResult *result,
         MF_OBJECT_TYPE *obj_type, IUnknown **object)
 {
-    struct file_scheme_handler *handler = impl_from_IMFSchemeHandler(iface);
-    struct file_scheme_handler_result *found = NULL, *cur;
-    HRESULT hr;
+    struct file_scheme_handler *this = impl_from_IMFSchemeHandler(iface);
 
     TRACE("%p, %p, %p, %p.\n", iface, result, obj_type, object);
-
-    EnterCriticalSection(&handler->cs);
-
-    LIST_FOR_EACH_ENTRY(cur, &handler->results, struct file_scheme_handler_result, entry)
-    {
-        if (result == cur->result)
-        {
-            list_remove(&cur->entry);
-            found = cur;
-            break;
-        }
-    }
-
-    LeaveCriticalSection(&handler->cs);
-
-    if (found)
-    {
-        *obj_type = found->obj_type;
-        *object = found->object;
-        hr = IMFAsyncResult_GetStatus(found->result);
-        IMFAsyncResult_Release(found->result);
-        heap_free(found);
-    }
-    else
-    {
-        *obj_type = MF_OBJECT_INVALID;
-        *object = NULL;
-        hr = MF_E_UNEXPECTED;
-    }
-
-    return hr;
+    return handler_end_create_object(&this->handler, result, obj_type, object);
 }
 
 static HRESULT WINAPI file_scheme_handler_CancelObjectCreation(IMFSchemeHandler *iface, IUnknown *cancel_cookie)
 {
-    struct file_scheme_handler *handler = impl_from_IMFSchemeHandler(iface);
-    struct file_scheme_handler_result *found = NULL, *cur;
+    struct file_scheme_handler *this = impl_from_IMFSchemeHandler(iface);
 
     TRACE("%p, %p.\n", iface, cancel_cookie);
-
-    EnterCriticalSection(&handler->cs);
-
-    LIST_FOR_EACH_ENTRY(cur, &handler->results, struct file_scheme_handler_result, entry)
-    {
-        if (cancel_cookie == (IUnknown *)cur->result)
-        {
-            list_remove(&cur->entry);
-            found = cur;
-            break;
-        }
-    }
-
-    LeaveCriticalSection(&handler->cs);
-
-    if (found)
-    {
-        IMFAsyncResult_Release(found->result);
-        if (found->object)
-            IUnknown_Release(found->object);
-        heap_free(found);
-    }
-
-    return found ? S_OK : MF_E_UNEXPECTED;
+    return handler_cancel_object_creation(&this->handler, cancel_cookie);
 }
 
 static const IMFSchemeHandlerVtbl file_scheme_handler_vtbl =
@@ -857,38 +646,6 @@ static const IMFSchemeHandlerVtbl file_scheme_handler_vtbl =
     file_scheme_handler_CancelObjectCreation,
 };
 
-static HRESULT WINAPI file_scheme_handler_callback_QueryInterface(IMFAsyncCallback *iface, REFIID riid, void **obj)
-{
-    if (IsEqualIID(riid, &IID_IMFAsyncCallback) ||
-            IsEqualIID(riid, &IID_IUnknown))
-    {
-        *obj = iface;
-        IMFAsyncCallback_AddRef(iface);
-        return S_OK;
-    }
-
-    WARN("Unsupported %s.\n", debugstr_guid(riid));
-    *obj = NULL;
-    return E_NOINTERFACE;
-}
-
-static ULONG WINAPI file_scheme_handler_callback_AddRef(IMFAsyncCallback *iface)
-{
-    struct file_scheme_handler *handler = impl_from_IMFAsyncCallback(iface);
-    return IMFSchemeHandler_AddRef(&handler->IMFSchemeHandler_iface);
-}
-
-static ULONG WINAPI file_scheme_handler_callback_Release(IMFAsyncCallback *iface)
-{
-    struct file_scheme_handler *handler = impl_from_IMFAsyncCallback(iface);
-    return IMFSchemeHandler_Release(&handler->IMFSchemeHandler_iface);
-}
-
-static HRESULT WINAPI file_scheme_handler_callback_GetParameters(IMFAsyncCallback *iface, DWORD *flags, DWORD *queue)
-{
-    return E_NOTIMPL;
-}
-
 static HRESULT file_scheme_handler_get_resolver(struct file_scheme_handler *handler, IMFSourceResolver **resolver)
 {
     HRESULT hr;
@@ -910,111 +667,63 @@ static HRESULT file_scheme_handler_get_resolver(struct file_scheme_handler *hand
     return S_OK;
 }
 
-static HRESULT WINAPI file_scheme_handler_callback_Invoke(IMFAsyncCallback *iface, IMFAsyncResult *result)
+static HRESULT file_scheme_handler_create_object(struct handler *handler, WCHAR *url, IMFByteStream *stream, DWORD flags,
+                                         IPropertyStore *props, IUnknown **out_object, MF_OBJECT_TYPE *out_obj_type)
 {
     static const WCHAR schemeW[] = {'f','i','l','e',':','/','/'};
-    struct file_scheme_handler *handler = impl_from_IMFAsyncCallback(iface);
-    struct file_scheme_handler_result *handler_result;
-    MF_OBJECT_TYPE obj_type = MF_OBJECT_INVALID;
-    IUnknown *object = NULL, *context_object;
-    struct create_object_context *context;
+    HRESULT hr = S_OK;
+    WCHAR *path;
+    IMFByteStream *file_byte_stream;
+    struct file_scheme_handler *this = CONTAINING_RECORD(handler, struct file_scheme_handler, handler);
     IMFSourceResolver *resolver;
-    IMFAsyncResult *caller;
-    IMFByteStream *stream;
-    const WCHAR *url;
-    HRESULT hr;
-
-    caller = (IMFAsyncResult *)IMFAsyncResult_GetStateNoAddRef(result);
-
-    if (FAILED(hr = IMFAsyncResult_GetObject(result, &context_object)))
-    {
-        WARN("Expected context set for callee result.\n");
-        return hr;
-    }
-
-    context = impl_from_IUnknown(context_object);
 
     /* Strip from scheme, MFCreateFile() won't be expecting it. */
-    url = context->url;
-    if (!wcsnicmp(context->url, schemeW, ARRAY_SIZE(schemeW)))
-        url += ARRAY_SIZE(schemeW);
+    path = url;
+    if (!wcsnicmp(url, schemeW, ARRAY_SIZE(schemeW)))
+        path += ARRAY_SIZE(schemeW);
 
-    hr = MFCreateFile(context->flags & MF_RESOLUTION_WRITE ? MF_ACCESSMODE_READWRITE : MF_ACCESSMODE_READ,
-            MF_OPENMODE_FAIL_IF_NOT_EXIST, MF_FILEFLAGS_NONE, url, &stream);
+    hr = MFCreateFile(flags & MF_RESOLUTION_WRITE ? MF_ACCESSMODE_READWRITE : MF_ACCESSMODE_READ,
+            MF_OPENMODE_FAIL_IF_NOT_EXIST, MF_FILEFLAGS_NONE, path, &file_byte_stream);
     if (SUCCEEDED(hr))
     {
-        if (context->flags & MF_RESOLUTION_MEDIASOURCE)
+        if (flags & MF_RESOLUTION_MEDIASOURCE)
         {
-            if (SUCCEEDED(hr = file_scheme_handler_get_resolver(handler, &resolver)))
+            if (SUCCEEDED(hr = file_scheme_handler_get_resolver(this, &resolver)))
             {
-                hr = IMFSourceResolver_CreateObjectFromByteStream(resolver, stream, context->url, context->flags,
-                        context->props, &obj_type, &object);
+                hr = IMFSourceResolver_CreateObjectFromByteStream(resolver, file_byte_stream, url, flags,
+                        props, out_obj_type, out_object);
                 IMFSourceResolver_Release(resolver);
-                IMFByteStream_Release(stream);
+                IMFByteStream_Release(file_byte_stream);
             }
         }
         else
         {
-            object = (IUnknown *)stream;
-            obj_type = MF_OBJECT_BYTESTREAM;
+            *out_object = (IUnknown *)file_byte_stream;
+            *out_obj_type = MF_OBJECT_BYTESTREAM;
         }
     }
 
-    handler_result = heap_alloc(sizeof(*handler_result));
-    if (handler_result)
-    {
-        handler_result->result = caller;
-        IMFAsyncResult_AddRef(handler_result->result);
-        handler_result->obj_type = obj_type;
-        handler_result->object = object;
-
-        EnterCriticalSection(&handler->cs);
-        list_add_tail(&handler->results, &handler_result->entry);
-        LeaveCriticalSection(&handler->cs);
-    }
-    else
-    {
-        if (object)
-            IUnknown_Release(object);
-        hr = E_OUTOFMEMORY;
-    }
-
-    IUnknown_Release(&context->IUnknown_iface);
-
-    IMFAsyncResult_SetStatus(caller, hr);
-    MFInvokeCallback(caller);
-
-    return S_OK;
+    return hr;
 }
 
-static const IMFAsyncCallbackVtbl file_scheme_handler_callback_vtbl =
-{
-    file_scheme_handler_callback_QueryInterface,
-    file_scheme_handler_callback_AddRef,
-    file_scheme_handler_callback_Release,
-    file_scheme_handler_callback_GetParameters,
-    file_scheme_handler_callback_Invoke,
-};
-
 static HRESULT file_scheme_handler_construct(REFIID riid, void **obj)
 {
-    struct file_scheme_handler *handler;
+    struct file_scheme_handler *this;
     HRESULT hr;
 
     TRACE("%s, %p.\n", debugstr_guid(riid), obj);
 
-    handler = heap_alloc_zero(sizeof(*handler));
-    if (!handler)
+    this = heap_alloc_zero(sizeof(*this));
+    if (!this)
         return E_OUTOFMEMORY;
 
-    handler->IMFSchemeHandler_iface.lpVtbl = &file_scheme_handler_vtbl;
-    handler->IMFAsyncCallback_iface.lpVtbl = &file_scheme_handler_callback_vtbl;
-    handler->refcount = 1;
-    list_init(&handler->results);
-    InitializeCriticalSection(&handler->cs);
+    handler_construct(&this->handler, file_scheme_handler_create_object);
+
+    this->IMFSchemeHandler_iface.lpVtbl = &file_scheme_handler_vtbl;
+    this->refcount = 1;
 
-    hr = IMFSchemeHandler_QueryInterface(&handler->IMFSchemeHandler_iface, riid, obj);
-    IMFSchemeHandler_Release(&handler->IMFSchemeHandler_iface);
+    hr = IMFSchemeHandler_QueryInterface(&this->IMFSchemeHandler_iface, riid, obj);
+    IMFSchemeHandler_Release(&this->IMFSchemeHandler_iface);
 
     return hr;
 }
diff --git a/dlls/winegstreamer/Makefile.in b/dlls/winegstreamer/Makefile.in
index e578d194f7..f2e8749445 100644
--- a/dlls/winegstreamer/Makefile.in
+++ b/dlls/winegstreamer/Makefile.in
@@ -3,12 +3,13 @@ IMPORTS   = strmiids uuid ole32 mfuuid
 DELAYIMPORTS = mfplat
 EXTRAINCL = $(GSTREAMER_CFLAGS)
 EXTRALIBS = $(GSTREAMER_LIBS) $(PTHREAD_LIBS)
-PARENTSRC = ../strmbase
+PARENTSRC = ../strmbase ../mf
 
 C_SRCS = \
 	filter.c \
 	gst_cbs.c \
 	gstdemux.c \
+	handler.c \
 	main.c \
 	media_source.c \
 	mediatype.c \
diff --git a/dlls/winegstreamer/media_source.c b/dlls/winegstreamer/media_source.c
index b88f794d86..ebb88d2b8a 100644
--- a/dlls/winegstreamer/media_source.c
+++ b/dlls/winegstreamer/media_source.c
@@ -4,6 +4,7 @@
 
 #include "gst_private.h"
 #include "gst_cbs.h"
+#include "handler.h"
 
 #include <assert.h>
 #include <stdarg.h>
@@ -1484,22 +1485,12 @@ static HRESULT media_source_constructor(IMFByteStream *bytestream, enum source_t
 
 /* IMFByteStreamHandler */
 
-struct container_stream_handler_result
-{
-    struct list entry;
-    IMFAsyncResult *result;
-    MF_OBJECT_TYPE obj_type;
-    IUnknown *object;
-};
-
 struct container_stream_handler
 {
     IMFByteStreamHandler IMFByteStreamHandler_iface;
-    IMFAsyncCallback IMFAsyncCallback_iface;
     LONG refcount;
     enum source_type type;
-    struct list results;
-    CRITICAL_SECTION cs;
+    struct handler handler;
 };
 
 static struct container_stream_handler *impl_from_IMFByteStreamHandler(IMFByteStreamHandler *iface)
@@ -1507,11 +1498,6 @@ static struct container_stream_handler *impl_from_IMFByteStreamHandler(IMFByteSt
     return CONTAINING_RECORD(iface, struct container_stream_handler, IMFByteStreamHandler_iface);
 }
 
-static struct container_stream_handler *impl_from_IMFAsyncCallback(IMFAsyncCallback *iface)
-{
-    return CONTAINING_RECORD(iface, struct container_stream_handler, IMFAsyncCallback_iface);
-}
-
 static HRESULT WINAPI container_stream_handler_QueryInterface(IMFByteStreamHandler *iface, REFIID riid, void **obj)
 {
     TRACE("%p, %s, %p.\n", iface, debugstr_guid(riid), obj);
@@ -1541,248 +1527,44 @@ static ULONG WINAPI container_stream_handler_AddRef(IMFByteStreamHandler *iface)
 
 static ULONG WINAPI container_stream_handler_Release(IMFByteStreamHandler *iface)
 {
-    struct container_stream_handler *handler = impl_from_IMFByteStreamHandler(iface);
-    ULONG refcount = InterlockedDecrement(&handler->refcount);
-    struct container_stream_handler_result *result, *next;
-
-    TRACE("%p, refcount %u.\n", iface, refcount);
-
-    if (!refcount)
-    {
-        LIST_FOR_EACH_ENTRY_SAFE(result, next, &handler->results, struct container_stream_handler_result, entry)
-        {
-            list_remove(&result->entry);
-            IMFAsyncResult_Release(result->result);
-            if (result->object)
-                IUnknown_Release(result->object);
-            heap_free(result);
-        }
-        DeleteCriticalSection(&handler->cs);
-        heap_free(handler);
-    }
-
-    return refcount;
-}
-
-struct create_object_context
-{
-    IUnknown IUnknown_iface;
-    LONG refcount;
-
-    IPropertyStore *props;
-    IMFByteStream *stream;
-    WCHAR *url;
-    DWORD flags;
-};
-
-static struct create_object_context *impl_from_IUnknown(IUnknown *iface)
-{
-    return CONTAINING_RECORD(iface, struct create_object_context, IUnknown_iface);
-}
-
-static HRESULT WINAPI create_object_context_QueryInterface(IUnknown *iface, REFIID riid, void **obj)
-{
-    TRACE("%p, %s, %p.\n", iface, debugstr_guid(riid), obj);
-
-    if (IsEqualIID(riid, &IID_IUnknown))
-    {
-        *obj = iface;
-        IUnknown_AddRef(iface);
-        return S_OK;
-    }
-
-    WARN("Unsupported %s.\n", debugstr_guid(riid));
-    *obj = NULL;
-    return E_NOINTERFACE;
-}
-
-static ULONG WINAPI create_object_context_AddRef(IUnknown *iface)
-{
-    struct create_object_context *context = impl_from_IUnknown(iface);
-    ULONG refcount = InterlockedIncrement(&context->refcount);
-
-    TRACE("%p, refcount %u.\n", iface, refcount);
-
-    return refcount;
-}
-
-static ULONG WINAPI create_object_context_Release(IUnknown *iface)
-{
-    struct create_object_context *context = impl_from_IUnknown(iface);
-    ULONG refcount = InterlockedDecrement(&context->refcount);
+    struct container_stream_handler *this = impl_from_IMFByteStreamHandler(iface);
+    ULONG refcount = InterlockedDecrement(&this->refcount);
 
     TRACE("%p, refcount %u.\n", iface, refcount);
 
     if (!refcount)
     {
-        if (context->props)
-            IPropertyStore_Release(context->props);
-        if (context->stream)
-            IMFByteStream_Release(context->stream);
-        if (context->url)
-            heap_free(context->url);
-        heap_free(context);
+        handler_destruct(&this->handler);
+        heap_free(this);
     }
 
     return refcount;
 }
 
-static const IUnknownVtbl create_object_context_vtbl =
-{
-    create_object_context_QueryInterface,
-    create_object_context_AddRef,
-    create_object_context_Release,
-};
-
-static WCHAR *heap_strdupW(const WCHAR *str)
-{
-    WCHAR *ret = NULL;
-
-    if (str)
-    {
-        unsigned int size;
-
-        size = (lstrlenW(str) + 1) * sizeof(WCHAR);
-        ret = heap_alloc(size);
-        if (ret)
-            memcpy(ret, str, size);
-    }
-
-    return ret;
-}
-
 static HRESULT WINAPI container_stream_handler_BeginCreateObject(IMFByteStreamHandler *iface, IMFByteStream *stream, const WCHAR *url, DWORD flags,
         IPropertyStore *props, IUnknown **cancel_cookie, IMFAsyncCallback *callback, IUnknown *state)
 {
     struct container_stream_handler *this = impl_from_IMFByteStreamHandler(iface);
-    struct create_object_context *context;
-    IMFAsyncResult *caller, *item;
-    HRESULT hr;
 
     TRACE("%p, %s, %#x, %p, %p, %p, %p.\n", iface, debugstr_w(url), flags, props, cancel_cookie, callback, state);
-
-    if (cancel_cookie)
-        *cancel_cookie = NULL;
-
-    if (FAILED(hr = MFCreateAsyncResult(NULL, callback, state, &caller)))
-        return hr;
-
-    context = heap_alloc(sizeof(*context));
-    if (!context)
-    {
-        IMFAsyncResult_Release(caller);
-        return E_OUTOFMEMORY;
-    }
-
-    context->IUnknown_iface.lpVtbl = &create_object_context_vtbl;
-    context->refcount = 1;
-    context->props = props;
-    if (context->props)
-        IPropertyStore_AddRef(context->props);
-    context->flags = flags;
-    context->stream = stream;
-    if (context->stream)
-        IMFByteStream_AddRef(context->stream);
-    if (url)
-        context->url = heap_strdupW(url);
-    if (!context->stream)
-    {
-        IMFAsyncResult_Release(caller);
-        IUnknown_Release(&context->IUnknown_iface);
-        return E_OUTOFMEMORY;
-    }
-
-    hr = MFCreateAsyncResult(&context->IUnknown_iface, &this->IMFAsyncCallback_iface, (IUnknown *)caller, &item);
-    IUnknown_Release(&context->IUnknown_iface);
-    if (SUCCEEDED(hr))
-    {
-        if (SUCCEEDED(hr = MFPutWorkItemEx(MFASYNC_CALLBACK_QUEUE_IO, item)))
-        {
-            if (cancel_cookie)
-            {
-                *cancel_cookie = (IUnknown *)caller;
-                IUnknown_AddRef(*cancel_cookie);
-            }
-        }
-
-        IMFAsyncResult_Release(item);
-    }
-    IMFAsyncResult_Release(caller);
-
-    return hr;
+    return handler_begin_create_object(&this->handler, stream, url, flags, props, cancel_cookie, callback, state);
 }
 
 static HRESULT WINAPI container_stream_handler_EndCreateObject(IMFByteStreamHandler *iface, IMFAsyncResult *result,
         MF_OBJECT_TYPE *obj_type, IUnknown **object)
 {
     struct container_stream_handler *this = impl_from_IMFByteStreamHandler(iface);
-    struct container_stream_handler_result *found = NULL, *cur;
-    HRESULT hr;
 
     TRACE("%p, %p, %p, %p.\n", iface, result, obj_type, object);
-
-    EnterCriticalSection(&this->cs);
-
-    LIST_FOR_EACH_ENTRY(cur, &this->results, struct container_stream_handler_result, entry)
-    {
-        if (result == cur->result)
-        {
-            list_remove(&cur->entry);
-            found = cur;
-            break;
-        }
-    }
-
-    LeaveCriticalSection(&this->cs);
-
-    if (found)
-    {
-        *obj_type = found->obj_type;
-        *object = found->object;
-        hr = IMFAsyncResult_GetStatus(found->result);
-        IMFAsyncResult_Release(found->result);
-        heap_free(found);
-    }
-    else
-    {
-        *obj_type = MF_OBJECT_INVALID;
-        *object = NULL;
-        hr = MF_E_UNEXPECTED;
-    }
-
-    return hr;
+    return handler_end_create_object(&this->handler, result, obj_type, object);
 }
 
 static HRESULT WINAPI container_stream_handler_CancelObjectCreation(IMFByteStreamHandler *iface, IUnknown *cancel_cookie)
 {
     struct container_stream_handler *this = impl_from_IMFByteStreamHandler(iface);
-    struct container_stream_handler_result *found = NULL, *cur;
 
     TRACE("%p, %p.\n", iface, cancel_cookie);
-
-    EnterCriticalSection(&this->cs);
-
-    LIST_FOR_EACH_ENTRY(cur, &this->results, struct container_stream_handler_result, entry)
-    {
-        if (cancel_cookie == (IUnknown *)cur->result)
-        {
-            list_remove(&cur->entry);
-            found = cur;
-            break;
-        }
-    }
-
-    LeaveCriticalSection(&this->cs);
-
-    if (found)
-    {
-        IMFAsyncResult_Release(found->result);
-        if (found->object)
-            IUnknown_Release(found->object);
-        heap_free(found);
-    }
-
-    return found ? S_OK : MF_E_UNEXPECTED;
+    return handler_cancel_object_creation(&this->handler, cancel_cookie);
 }
 
 static HRESULT WINAPI container_stream_handler_GetMaxNumberOfBytesRequiredForResolution(IMFByteStreamHandler *iface, QWORD *bytes)
@@ -1802,47 +1584,16 @@ static const IMFByteStreamHandlerVtbl container_stream_handler_vtbl =
     container_stream_handler_GetMaxNumberOfBytesRequiredForResolution,
 };
 
-static HRESULT WINAPI container_stream_handler_callback_QueryInterface(IMFAsyncCallback *iface, REFIID riid, void **obj)
-{
-    if (IsEqualIID(riid, &IID_IMFAsyncCallback) ||
-            IsEqualIID(riid, &IID_IUnknown))
-    {
-        *obj = iface;
-        IMFAsyncCallback_AddRef(iface);
-        return S_OK;
-    }
-
-    WARN("Unsupported %s.\n", debugstr_guid(riid));
-    *obj = NULL;
-    return E_NOINTERFACE;
-}
-
-static ULONG WINAPI container_stream_handler_callback_AddRef(IMFAsyncCallback *iface)
-{
-    struct container_stream_handler *handler = impl_from_IMFAsyncCallback(iface);
-    return IMFByteStreamHandler_AddRef(&handler->IMFByteStreamHandler_iface);
-}
-
-static ULONG WINAPI container_stream_handler_callback_Release(IMFAsyncCallback *iface)
-{
-    struct container_stream_handler *handler = impl_from_IMFAsyncCallback(iface);
-    return IMFByteStreamHandler_Release(&handler->IMFByteStreamHandler_iface);
-}
-
-static HRESULT WINAPI container_stream_handler_callback_GetParameters(IMFAsyncCallback *iface, DWORD *flags, DWORD *queue)
-{
-    return E_NOTIMPL;
-}
-
-static HRESULT container_stream_handler_create_object(struct container_stream_handler *This, WCHAR *url, IMFByteStream *stream, DWORD flags,
+static HRESULT container_stream_handler_create_object(struct handler *handler, WCHAR *url, IMFByteStream *stream, DWORD flags,
                                             IPropertyStore *props, IUnknown **out_object, MF_OBJECT_TYPE *out_obj_type)
 {
-    TRACE("(%p %s %p %u %p %p %p)\n", This, debugstr_w(url), stream, flags, props, out_object, out_obj_type);
+    TRACE("(%p %s %p %u %p %p %p)\n", handler, debugstr_w(url), stream, flags, props, out_object, out_obj_type);
 
     if (flags & MF_RESOLUTION_MEDIASOURCE)
     {
         HRESULT hr;
         struct media_source *new_source;
+        struct container_stream_handler *This = CONTAINING_RECORD(handler, struct container_stream_handler, handler);
 
         if (FAILED(hr = media_source_constructor(stream, This->type, &new_source)))
             return hr;
@@ -1861,64 +1612,6 @@ static HRESULT container_stream_handler_create_object(struct container_stream_ha
     }
 }
 
-static HRESULT WINAPI container_stream_handler_callback_Invoke(IMFAsyncCallback *iface, IMFAsyncResult *result)
-{
-    struct container_stream_handler *handler = impl_from_IMFAsyncCallback(iface);
-    struct container_stream_handler_result *handler_result;
-    MF_OBJECT_TYPE obj_type = MF_OBJECT_INVALID;
-    IUnknown *object = NULL, *context_object;
-    struct create_object_context *context;
-    IMFAsyncResult *caller;
-    HRESULT hr;
-
-    caller = (IMFAsyncResult *)IMFAsyncResult_GetStateNoAddRef(result);
-
-    if (FAILED(hr = IMFAsyncResult_GetObject(result, &context_object)))
-    {
-        WARN("Expected context set for callee result.\n");
-        return hr;
-    }
-
-    context = impl_from_IUnknown(context_object);
-
-    hr = container_stream_handler_create_object(handler, context->url, context->stream, context->flags, context->props, &object, &obj_type);
-
-    handler_result = heap_alloc(sizeof(*handler_result));
-    if (handler_result)
-    {
-        handler_result->result = caller;
-        IMFAsyncResult_AddRef(handler_result->result);
-        handler_result->obj_type = obj_type;
-        handler_result->object = object;
-
-        EnterCriticalSection(&handler->cs);
-        list_add_tail(&handler->results, &handler_result->entry);
-        LeaveCriticalSection(&handler->cs);
-    }
-    else
-    {
-        if (object)
-            IUnknown_Release(object);
-        hr = E_OUTOFMEMORY;
-    }
-
-    IUnknown_Release(&context->IUnknown_iface);
-
-    IMFAsyncResult_SetStatus(caller, hr);
-    MFInvokeCallback(caller);
-
-    return S_OK;
-}
-
-static const IMFAsyncCallbackVtbl container_stream_handler_callback_vtbl =
-{
-    container_stream_handler_callback_QueryInterface,
-    container_stream_handler_callback_AddRef,
-    container_stream_handler_callback_Release,
-    container_stream_handler_callback_GetParameters,
-    container_stream_handler_callback_Invoke,
-};
-
 HRESULT container_stream_handler_construct(REFIID riid, void **obj, enum source_type type)
 {
     struct container_stream_handler *this;
@@ -1930,12 +1623,10 @@ HRESULT container_stream_handler_construct(REFIID riid, void **obj, enum source_
     if (!this)
         return E_OUTOFMEMORY;
 
-    list_init(&this->results);
-    InitializeCriticalSection(&this->cs);
+    handler_construct(&this->handler, container_stream_handler_create_object);
 
     this->type = type;
     this->IMFByteStreamHandler_iface.lpVtbl = &container_stream_handler_vtbl;
-    this->IMFAsyncCallback_iface.lpVtbl = &container_stream_handler_callback_vtbl;
     this->refcount = 1;
 
     hr = IMFByteStreamHandler_QueryInterface(&this->IMFByteStreamHandler_iface, riid, obj);
-- 
2.25.3

From d0eadd8c7d18481d76e87a7472a6649515489e8e Mon Sep 17 00:00:00 2001
From: Derek Lesho <dlesho@codeweavers.com>
Date: Tue, 24 Mar 2020 16:15:35 -0500
Subject: [PATCH 23/46] winegstreamer: Introduce IMFMediaType -> GstCaps
 converter.

Signed-off-by: Derek Lesho <dlesho@codeweavers.com>
---
 dlls/winegstreamer/gst_private.h |   1 +
 dlls/winegstreamer/mfplat.c      | 139 +++++++++++++++++++++++++++++++
 2 files changed, 140 insertions(+)

diff --git a/dlls/winegstreamer/gst_private.h b/dlls/winegstreamer/gst_private.h
index 823e023f52..20d3beef02 100644
--- a/dlls/winegstreamer/gst_private.h
+++ b/dlls/winegstreamer/gst_private.h
@@ -57,6 +57,7 @@ extern HRESULT mfplat_get_class_object(REFCLSID rclsid, REFIID riid, void **obj)
 
 GstCaps *make_mf_compatible_caps(GstCaps *caps);
 IMFMediaType *mf_media_type_from_caps(GstCaps *caps);
+GstCaps *caps_from_mf_media_type(IMFMediaType *type);
 IMFSample *mf_sample_from_gst_buffer(GstBuffer *in);
 
 enum source_type
diff --git a/dlls/winegstreamer/mfplat.c b/dlls/winegstreamer/mfplat.c
index 81782eeab3..78ced2615c 100644
--- a/dlls/winegstreamer/mfplat.c
+++ b/dlls/winegstreamer/mfplat.c
@@ -873,6 +873,145 @@ GstCaps *make_mf_compatible_caps(GstCaps *caps)
     return ret;
 }
 
+GstCaps *caps_from_mf_media_type(IMFMediaType *type)
+{
+    GUID major_type;
+    GUID subtype;
+    GstCaps *output = NULL;
+
+    if (FAILED(IMFMediaType_GetMajorType(type, &major_type)))
+        return NULL;
+    if (FAILED(IMFMediaType_GetGUID(type, &MF_MT_SUBTYPE, &subtype)))
+        return NULL;
+
+    if (IsEqualGUID(&major_type, &MFMediaType_Video))
+    {
+        UINT64 frame_rate = 0, frame_size = 0;
+        DWORD width, height, framerate_num, framerate_den;
+        UINT32 unused;
+
+        if (FAILED(IMFMediaType_GetUINT64(type, &MF_MT_FRAME_SIZE, &frame_size)))
+            return NULL;
+        width = frame_size >> 32;
+        height = frame_size;
+        if (FAILED(IMFMediaType_GetUINT64(type, &MF_MT_FRAME_RATE, &frame_rate)))
+        {
+            frame_rate = TRUE;
+            framerate_num = 0;
+            framerate_den = 1;
+        }
+        else
+        {
+            framerate_num = frame_rate >> 32;
+            framerate_den = frame_rate;
+        }
+
+        /* Check if type is uncompressed */
+        if (SUCCEEDED(MFCalculateImageSize(&subtype, 100, 100, &unused)))
+        {
+            GstVideoFormat format = GST_VIDEO_FORMAT_UNKNOWN;
+            unsigned int i;
+
+            output = gst_caps_new_empty_simple("video/x-raw");
+
+            for (i = 0; i < ARRAY_SIZE(uncompressed_formats); i++)
+            {
+                if (IsEqualGUID(uncompressed_formats[i].subtype, &subtype))
+                {
+                    format = uncompressed_formats[i].format;
+                    break;
+                }
+            }
+
+            if (format == GST_VIDEO_FORMAT_UNKNOWN)
+            {
+                format = gst_video_format_from_fourcc(subtype.Data1);
+            }
+
+            if (format == GST_VIDEO_FORMAT_UNKNOWN)
+            {
+                FIXME("Unrecognized format %s\n", debugstr_guid(&subtype));
+                return NULL;
+            }
+            else
+            {
+                GstVideoInfo info;
+
+                gst_video_info_set_format(&info, format, width, height);
+                output = gst_video_info_to_caps(&info);
+            }
+
+        }
+        else {
+            FIXME("Unrecognized subtype %s\n", debugstr_guid(&subtype));
+            return NULL;
+        }
+
+
+        if (frame_size)
+        {
+            gst_caps_set_simple(output, "width", G_TYPE_INT, width, NULL);
+            gst_caps_set_simple(output, "height", G_TYPE_INT, height, NULL);
+        }
+        if (frame_rate)
+            gst_caps_set_simple(output, "framerate", GST_TYPE_FRACTION, framerate_num, framerate_den, NULL);
+        return output;
+    }
+    else if (IsEqualGUID(&major_type, &MFMediaType_Audio))
+    {
+        DWORD rate, channels;
+
+        if (IsEqualGUID(&subtype, &MFAudioFormat_Float))
+        {
+            output = gst_caps_new_empty_simple("audio/x-raw");
+
+            gst_caps_set_simple(output, "format", G_TYPE_STRING, "F32LE", NULL);
+        }
+        else if (IsEqualGUID(&subtype, &MFAudioFormat_PCM))
+        {
+            DWORD bits_per_sample;
+
+            if (SUCCEEDED(IMFMediaType_GetUINT32(type, &MF_MT_AUDIO_BITS_PER_SAMPLE, &bits_per_sample)))
+            {
+                char format[6];
+                char type;
+
+                type = bits_per_sample > 8 ? 'S' : 'U';
+
+                output = gst_caps_new_empty_simple("audio/x-raw");
+
+                sprintf(format, "%c%u%s", type, bits_per_sample, bits_per_sample > 8 ? "LE" : "");
+
+                gst_caps_set_simple(output, "format", G_TYPE_STRING, format, NULL);
+            }
+            else
+            {
+                ERR("Bits per sample not set.\n");
+                return NULL;
+            }
+        }
+        else
+        {
+            FIXME("Unrecognized subtype %s\n", debugstr_guid(&subtype));
+            return NULL;
+        }
+
+        if (SUCCEEDED(IMFMediaType_GetUINT32(type, &MF_MT_AUDIO_SAMPLES_PER_SECOND, &rate)))
+        {
+            gst_caps_set_simple(output, "rate", G_TYPE_INT, rate, NULL);
+        }
+        if (SUCCEEDED(IMFMediaType_GetUINT32(type, &MF_MT_AUDIO_NUM_CHANNELS, &channels)))
+        {
+            gst_caps_set_simple(output, "channels", G_TYPE_INT, channels, NULL);
+        }
+
+        return output;
+    }
+
+    FIXME("Unrecognized major type %s\n", debugstr_guid(&major_type));
+    return NULL;
+}
+
 /* IMFSample = GstBuffer
    IMFBuffer = GstMemory */
 
-- 
2.25.3

From a30b82175fdb9256fe94b031e58cc96920b00b57 Mon Sep 17 00:00:00 2001
From: Derek Lesho <dlesho@codeweavers.com>
Date: Tue, 24 Mar 2020 16:18:40 -0500
Subject: [PATCH 24/46] winegstreamer: Translate H.264 attributes to caps.

Signed-off-by: Derek Lesho <dlesho@codeweavers.com>
---
 dlls/winegstreamer/mfplat.c | 48 +++++++++++++++++++++++++++++++++++++
 1 file changed, 48 insertions(+)

diff --git a/dlls/winegstreamer/mfplat.c b/dlls/winegstreamer/mfplat.c
index 78ced2615c..1e2a98b7d5 100644
--- a/dlls/winegstreamer/mfplat.c
+++ b/dlls/winegstreamer/mfplat.c
@@ -942,6 +942,54 @@ GstCaps *caps_from_mf_media_type(IMFMediaType *type)
             }
 
         }
+        else if (IsEqualGUID(&subtype, &MFVideoFormat_H264))
+        {
+            enum eAVEncH264VProfile h264_profile;
+            enum eAVEncH264VLevel h264_level;
+            output = gst_caps_new_empty_simple("video/x-h264");
+            gst_caps_set_simple(output, "stream-format", G_TYPE_STRING, "byte-stream", NULL);
+            gst_caps_set_simple(output, "alignment", G_TYPE_STRING, "au", NULL);
+
+            if (SUCCEEDED(IMFMediaType_GetUINT32(type, &MF_MT_MPEG2_PROFILE, &h264_profile)))
+            {
+                const char *profile = NULL;
+                switch (h264_profile)
+                {
+                    case eAVEncH264VProfile_Main: profile = "main"; break;
+                    case eAVEncH264VProfile_High: profile = "high"; break;
+                    case eAVEncH264VProfile_444: profile = "high-4:4:4"; break;
+                    default: FIXME("Unknown profile %u\n", h264_profile);
+                }
+                if (profile)
+                    gst_caps_set_simple(output, "profile", G_TYPE_STRING, profile, NULL);
+            }
+            if (SUCCEEDED(IMFMediaType_GetUINT32(type, &MF_MT_MPEG2_LEVEL, &h264_level)))
+            {
+                const char *level = NULL;
+                switch (h264_level)
+                {
+                    case eAVEncH264VLevel1:   level = "1";   break;
+                    case eAVEncH264VLevel1_1: level = "1.1"; break;
+                    case eAVEncH264VLevel1_2: level = "1.2"; break;
+                    case eAVEncH264VLevel1_3: level = "1.3"; break;
+                    case eAVEncH264VLevel2:   level = "2";   break;
+                    case eAVEncH264VLevel2_1: level = "2.1"; break;
+                    case eAVEncH264VLevel2_2: level = "2.2"; break;
+                    case eAVEncH264VLevel3:   level = "3";   break;
+                    case eAVEncH264VLevel3_1: level = "3.1"; break;
+                    case eAVEncH264VLevel3_2: level = "3.2"; break;
+                    case eAVEncH264VLevel4:   level = "4";   break;
+                    case eAVEncH264VLevel4_1: level = "4.1"; break;
+                    case eAVEncH264VLevel4_2: level = "4.2"; break;
+                    case eAVEncH264VLevel5:   level = "5";   break;
+                    case eAVEncH264VLevel5_1: level = "5.1"; break;
+                    case eAVEncH264VLevel5_2: level = "5.2"; break;
+                    default: FIXME("Unknown level %u\n", h264_level);
+                }
+                if (level)
+                    gst_caps_set_simple(output, "level", G_TYPE_STRING, level, NULL);
+            }
+        }
         else {
             FIXME("Unrecognized subtype %s\n", debugstr_guid(&subtype));
             return NULL;
-- 
2.25.3

From a60936d9611ee2bd976f9da014e641e2959493d6 Mon Sep 17 00:00:00 2001
From: Derek Lesho <dlesho@codeweavers.com>
Date: Tue, 24 Mar 2020 16:20:17 -0500
Subject: [PATCH 25/46] winegstreamer: Translate WMV attributes to caps.

Signed-off-by: Derek Lesho <dlesho@codeweavers.com>
---
 dlls/winegstreamer/mfplat.c | 18 ++++++++++++++++++
 1 file changed, 18 insertions(+)

diff --git a/dlls/winegstreamer/mfplat.c b/dlls/winegstreamer/mfplat.c
index 1e2a98b7d5..28fa286eb6 100644
--- a/dlls/winegstreamer/mfplat.c
+++ b/dlls/winegstreamer/mfplat.c
@@ -990,6 +990,24 @@ GstCaps *caps_from_mf_media_type(IMFMediaType *type)
                     gst_caps_set_simple(output, "level", G_TYPE_STRING, level, NULL);
             }
         }
+        else if (IsEqualGUID(&subtype, &MFVideoFormat_WVC1))
+        {
+            BYTE *user_data;
+            DWORD user_data_size;
+            output = gst_caps_new_empty_simple("video/x-wmv");
+            gst_caps_set_simple(output, "format", G_TYPE_STRING, "WVC1", NULL);
+
+            gst_caps_set_simple(output, "wmvversion", G_TYPE_INT, 3, NULL);
+
+            if (SUCCEEDED(IMFMediaType_GetAllocatedBlob(type, &MF_MT_USER_DATA, &user_data, &user_data_size)))
+            {
+                GstBuffer *codec_data_buffer = gst_buffer_new_allocate(NULL, user_data_size, NULL);
+                gst_buffer_fill(codec_data_buffer, 0, user_data, user_data_size);
+                gst_caps_set_simple(output, "codec_data", GST_TYPE_BUFFER, codec_data_buffer, NULL);
+                gst_buffer_unref(codec_data_buffer);
+                CoTaskMemFree(user_data);
+            }
+        }
         else {
             FIXME("Unrecognized subtype %s\n", debugstr_guid(&subtype));
             return NULL;
-- 
2.25.3

From 17ca976869888e63b034ce249e63ff7df8daa304 Mon Sep 17 00:00:00 2001
From: Derek Lesho <dlesho@codeweavers.com>
Date: Tue, 21 Apr 2020 10:31:02 -0500
Subject: [PATCH 26/46] winegstreamer: Translate AAC attributes to caps.

Signed-off-by: Derek Lesho <dlesho@codeweavers.com>
---
 dlls/winegstreamer/mfplat.c | 66 +++++++++++++++++++++++++++++++++++++
 1 file changed, 66 insertions(+)

diff --git a/dlls/winegstreamer/mfplat.c b/dlls/winegstreamer/mfplat.c
index 28fa286eb6..63a20d0df0 100644
--- a/dlls/winegstreamer/mfplat.c
+++ b/dlls/winegstreamer/mfplat.c
@@ -1056,6 +1056,72 @@ GstCaps *caps_from_mf_media_type(IMFMediaType *type)
                 return NULL;
             }
         }
+        else if (IsEqualGUID(&subtype, &MFAudioFormat_AAC))
+        {
+            DWORD payload_type, indication;
+            struct aac_user_data *user_data;
+            UINT32 user_data_size;
+            output = gst_caps_new_empty_simple("audio/mpeg");
+
+            /* Unsure of how to differentiate between mpegversion 2 and 4 */
+            gst_caps_set_simple(output, "mpegversion", G_TYPE_INT, 4, NULL);
+
+            if (SUCCEEDED(IMFMediaType_GetUINT32(type, &MF_MT_AAC_PAYLOAD_TYPE, &payload_type)))
+            {
+                switch (payload_type)
+                {
+                    case 0:
+                        gst_caps_set_simple(output, "stream-format", G_TYPE_STRING, "raw", NULL);
+                        break;
+                    case 1:
+                        gst_caps_set_simple(output, "stream-format", G_TYPE_STRING, "adts", NULL);
+                        break;
+                    case 2:
+                        gst_caps_set_simple(output, "stream-format", G_TYPE_STRING, "adif", NULL);
+                        break;
+                    case 3:
+                        gst_caps_set_simple(output, "stream-format", G_TYPE_STRING, "loas", NULL);
+                        break;
+                    default:
+                        gst_caps_set_simple(output, "stream-format", G_TYPE_STRING, "raw", NULL);
+                }
+            }
+            else
+                gst_caps_set_simple(output, "stream-format", G_TYPE_STRING, "raw", NULL);
+
+            if (SUCCEEDED(IMFMediaType_GetUINT32(type, &MF_MT_AAC_AUDIO_PROFILE_LEVEL_INDICATION, &indication)))
+            {
+                const char *profile, *level;
+                switch (indication)
+                {
+                    case 0x29: profile = "lc"; level = "2";  break;
+                    case 0x2A: profile = "lc"; level = "4"; break;
+                    case 0x2B: profile = "lc"; level = "5"; break;
+                    default:
+                    {
+                        profile = level = NULL;
+                        FIXME("Unrecognized profile-level-indication %u\n", indication);
+                    }
+                }
+                if (profile)
+                    gst_caps_set_simple(output, "profile", G_TYPE_STRING, profile, NULL);
+                if (level)
+                    gst_caps_set_simple(output, "level", G_TYPE_STRING, level, NULL);
+            }
+
+            if (SUCCEEDED(IMFMediaType_GetAllocatedBlob(type, &MF_MT_USER_DATA, (BYTE **) &user_data, &user_data_size)))
+            {
+                if (user_data_size > sizeof(*user_data))
+                {
+                    GstBuffer *audio_specific_config = gst_buffer_new_allocate(NULL, user_data_size - sizeof(*user_data), NULL);
+                    gst_buffer_fill(audio_specific_config, 0, user_data + 1, user_data_size - sizeof(*user_data));
+
+                    gst_caps_set_simple(output, "codec_data", GST_TYPE_BUFFER, audio_specific_config, NULL);
+                    gst_buffer_unref(audio_specific_config);
+                }
+                CoTaskMemFree(user_data);
+            }
+        }
         else
         {
             FIXME("Unrecognized subtype %s\n", debugstr_guid(&subtype));
-- 
2.25.3

From 2d24a1bfda76f22f1861592a85601c4441d9004b Mon Sep 17 00:00:00 2001
From: Derek Lesho <dlesho@codeweavers.com>
Date: Wed, 25 Mar 2020 10:43:27 -0500
Subject: [PATCH 27/46] Introduce IMFSample -> GstBuffer converter.

Signed-off-by: Derek Lesho <dlesho@codeweavers.com>
---
 dlls/winegstreamer/gst_private.h |  1 +
 dlls/winegstreamer/mfplat.c      | 74 ++++++++++++++++++++++++++++++++
 2 files changed, 75 insertions(+)

diff --git a/dlls/winegstreamer/gst_private.h b/dlls/winegstreamer/gst_private.h
index 20d3beef02..258df0d040 100644
--- a/dlls/winegstreamer/gst_private.h
+++ b/dlls/winegstreamer/gst_private.h
@@ -59,6 +59,7 @@ GstCaps *make_mf_compatible_caps(GstCaps *caps);
 IMFMediaType *mf_media_type_from_caps(GstCaps *caps);
 GstCaps *caps_from_mf_media_type(IMFMediaType *type);
 IMFSample *mf_sample_from_gst_buffer(GstBuffer *in);
+GstBuffer *gst_buffer_from_mf_sample(IMFSample *in);
 
 enum source_type
 {
diff --git a/dlls/winegstreamer/mfplat.c b/dlls/winegstreamer/mfplat.c
index 63a20d0df0..f1c8e34cd2 100644
--- a/dlls/winegstreamer/mfplat.c
+++ b/dlls/winegstreamer/mfplat.c
@@ -1232,4 +1232,78 @@ IMFSample* mf_sample_from_gst_buffer(GstBuffer *gst_buffer)
     ERR("Failed to copy IMFSample to GstBuffer, hr = %#x\n", hr);
     IMFSample_Release(out);
     return NULL;
+}
+
+GstBuffer* gst_buffer_from_mf_sample(IMFSample *mf_sample)
+{
+    GstBuffer *out = gst_buffer_new();
+    IMFMediaBuffer *mf_buffer = NULL;
+    LONGLONG duration, time;
+    DWORD buffer_count;
+    HRESULT hr;
+
+    if (FAILED(hr = IMFSample_GetSampleDuration(mf_sample, &duration)))
+        goto fail;
+
+    if (FAILED(hr = IMFSample_GetSampleTime(mf_sample, &time)))
+        goto fail;
+
+    GST_BUFFER_DURATION(out) = duration;
+    GST_BUFFER_PTS(out) = time * 100;
+
+    if (FAILED(hr = IMFSample_GetBufferCount(mf_sample, &buffer_count)))
+        goto fail;
+
+    for (unsigned int i = 0; i < buffer_count; i++)
+    {
+        DWORD buffer_max_size, buffer_size;
+        GstMapInfo map_info;
+        GstMemory *memory;
+        BYTE *buf_data;
+
+        if (FAILED(hr = IMFSample_GetBufferByIndex(mf_sample, i, &mf_buffer)))
+            goto fail;
+
+        if (FAILED(hr = IMFMediaBuffer_GetMaxLength(mf_buffer, &buffer_max_size)))
+            goto fail;
+
+        if (FAILED(hr = IMFMediaBuffer_GetCurrentLength(mf_buffer, &buffer_size)))
+            goto fail;
+
+        memory = gst_allocator_alloc(NULL, buffer_size, NULL);
+        gst_memory_resize(memory, 0, buffer_size);
+
+        if (!(gst_memory_map(memory, &map_info, GST_MAP_WRITE)))
+        {
+            hr = E_FAIL;
+            goto fail;
+        }
+
+        if (FAILED(hr = IMFMediaBuffer_Lock(mf_buffer, &buf_data, NULL, NULL)))
+            goto fail;
+
+        memcpy(map_info.data, buf_data, buffer_size);
+
+        if (FAILED(hr = IMFMediaBuffer_Unlock(mf_buffer)))
+            goto fail;
+
+        if (FAILED(hr = IMFMediaBuffer_SetCurrentLength(mf_buffer, buffer_size)))
+            goto fail;
+
+        gst_memory_unmap(memory, &map_info);
+
+        gst_buffer_append_memory(out, memory);
+
+        IMFMediaBuffer_Release(mf_buffer);
+        mf_buffer = NULL;
+    }
+
+    return out;
+
+    fail:
+    ERR("Failed to copy IMFSample to GstBuffer, hr = %#x\n", hr);
+    if (mf_buffer)
+        IMFMediaBuffer_Release(mf_buffer);
+    gst_buffer_unref(out);
+    return NULL;
 }
\ No newline at end of file
-- 
2.25.3

From d0a7517ca9b152e0a7b645e0b93688522516f7bb Mon Sep 17 00:00:00 2001
From: Derek Lesho <dlesho@codeweavers.com>
Date: Mon, 16 Mar 2020 12:09:39 -0500
Subject: [PATCH 28/46] winegstreamer: Implement decoder MFT on gstreamer.

---
 dlls/winegstreamer/Makefile.in               |    1 +
 dlls/winegstreamer/gst_cbs.c                 |   65 +
 dlls/winegstreamer/gst_cbs.h                 |   18 +
 dlls/winegstreamer/gst_private.h             |    7 +
 dlls/winegstreamer/main.c                    |    3 +-
 dlls/winegstreamer/mf_decode.c               | 1320 ++++++++++++++++++
 dlls/winegstreamer/mfplat.c                  |  118 +-
 dlls/winegstreamer/winegstreamer_classes.idl |   12 +
 include/mfidl.idl                            |    4 +-
 9 files changed, 1545 insertions(+), 3 deletions(-)
 create mode 100644 dlls/winegstreamer/mf_decode.c

diff --git a/dlls/winegstreamer/Makefile.in b/dlls/winegstreamer/Makefile.in
index f2e8749445..e2af408582 100644
--- a/dlls/winegstreamer/Makefile.in
+++ b/dlls/winegstreamer/Makefile.in
@@ -13,6 +13,7 @@ C_SRCS = \
 	main.c \
 	media_source.c \
 	mediatype.c \
+	mf_decode.c \
 	mfplat.c \
 	pin.c \
 	qualitycontrol.c \
diff --git a/dlls/winegstreamer/gst_cbs.c b/dlls/winegstreamer/gst_cbs.c
index 808c9f7ad2..64dddaf47c 100644
--- a/dlls/winegstreamer/gst_cbs.c
+++ b/dlls/winegstreamer/gst_cbs.c
@@ -51,6 +51,8 @@ static void CALLBACK perform_cb(TP_CALLBACK_INSTANCE *instance, void *user)
         perform_cb_gstdemux(cbdata);
     else if (cbdata->type < MEDIA_SOURCE_MAX)
         perform_cb_media_source(cbdata);
+    else if (cbdata->type < MF_DECODE_MAX)
+        perform_cb_mf_decode(cbdata);
 
     pthread_mutex_lock(&cbdata->lock);
     cbdata->finished = 1;
@@ -414,5 +416,68 @@ GstFlowReturn stream_new_sample_wrapper(GstElement *appsink, gpointer user)
 
     call_cb(&cbdata);
 
+    return cbdata.u.new_sample_data.ret;
+}
+
+gboolean activate_push_mode_wrapper(GstPad *pad, GstObject *parent, GstPadMode mode, gboolean activate)
+{
+    struct cb_data cbdata = { ACTIVATE_PUSH_MODE };
+
+    cbdata.u.activate_mode_data.pad = pad;
+    cbdata.u.activate_mode_data.parent = parent;
+    cbdata.u.activate_mode_data.mode = mode;
+    cbdata.u.activate_mode_data.activate = activate;
+
+    call_cb(&cbdata);
+
+    return cbdata.u.query_function_data.ret;
+}
+
+gboolean query_input_src_wrapper(GstPad *pad, GstObject *parent, GstQuery *query)
+{
+    struct cb_data cbdata = { QUERY_INPUT_SRC };
+
+    cbdata.u.query_function_data.pad = pad;
+    cbdata.u.query_function_data.parent = parent;
+    cbdata.u.query_function_data.query = query;
+
+    call_cb(&cbdata);
+
+    return cbdata.u.query_function_data.ret;
+}
+
+GstBusSyncReply watch_decoder_bus_wrapper(GstBus *bus, GstMessage *message, gpointer user)
+{
+    struct cb_data cbdata = { WATCH_DECODER_BUS };
+
+    cbdata.u.watch_bus_data.bus = bus;
+    cbdata.u.watch_bus_data.msg = message;
+    cbdata.u.watch_bus_data.user = user;
+
+    call_cb(&cbdata);
+
+    return cbdata.u.watch_bus_data.ret;
+}
+
+void decoder_pad_added_wrapper(GstElement *element, GstPad *pad, gpointer user)
+{
+    struct cb_data cbdata = { DECODER_PAD_ADDED };
+
+    cbdata.u.pad_added_data.element = element;
+    cbdata.u.pad_added_data.pad = pad;
+    cbdata.u.pad_added_data.user = user;
+
+    call_cb(&cbdata);
+}
+
+GstFlowReturn decoder_new_sample_wrapper(GstElement *appsink, gpointer user)
+{
+    struct cb_data cbdata = {DECODER_NEW_SAMPLE};
+
+    cbdata.u.new_sample_data.appsink = appsink;
+    cbdata.u.new_sample_data.user = user;
+
+    call_cb(&cbdata);
+
     return cbdata.u.new_sample_data.ret;
 }
\ No newline at end of file
diff --git a/dlls/winegstreamer/gst_cbs.h b/dlls/winegstreamer/gst_cbs.h
index 106368a064..a35d6d8830 100644
--- a/dlls/winegstreamer/gst_cbs.h
+++ b/dlls/winegstreamer/gst_cbs.h
@@ -54,6 +54,12 @@ enum CB_TYPE {
     SOURCE_ALL_STREAMS,
     STREAM_NEW_SAMPLE,
     MEDIA_SOURCE_MAX,
+    ACTIVATE_PUSH_MODE,
+    QUERY_INPUT_SRC,
+    DECODER_NEW_SAMPLE,
+    WATCH_DECODER_BUS,
+    DECODER_PAD_ADDED,
+    MF_DECODE_MAX,
 };
 
 struct cb_data {
@@ -143,6 +149,12 @@ struct cb_data {
             gpointer user;
             GstFlowReturn ret;
         } new_sample_data;
+        struct chain_data {
+            GstPad *pad;
+            GstObject *parent;
+            GstBuffer *buffer;
+            GstFlowReturn ret;
+        } chain_data;
     } u;
 
     int finished;
@@ -154,6 +166,7 @@ struct cb_data {
 void mark_wine_thread(void) DECLSPEC_HIDDEN;
 void perform_cb_gstdemux(struct cb_data *data) DECLSPEC_HIDDEN;
 void perform_cb_media_source(struct cb_data *data) DECLSPEC_HIDDEN;
+void perform_cb_mf_decode(struct cb_data *data) DECLSPEC_HIDDEN;
 
 GstBusSyncReply watch_bus_wrapper(GstBus *bus, GstMessage *msg, gpointer user) DECLSPEC_HIDDEN;
 void existing_new_pad_wrapper(GstElement *bin, GstPad *pad, gpointer user) DECLSPEC_HIDDEN;
@@ -179,5 +192,10 @@ void source_stream_added_wrapper(GstElement *bin, GstPad *pad, gpointer user) DE
 void source_stream_removed_wrapper(GstElement *element, GstPad *pad, gpointer user) DECLSPEC_HIDDEN;
 void source_all_streams_wrapper(GstElement *element, gpointer user) DECLSPEC_HIDDEN;
 GstFlowReturn stream_new_sample_wrapper(GstElement *appsink, gpointer user) DECLSPEC_HIDDEN;
+gboolean activate_push_mode_wrapper(GstPad *pad, GstObject *parent, GstPadMode mode, gboolean activate) DECLSPEC_HIDDEN;
+gboolean query_input_src_wrapper(GstPad *pad, GstObject *parent, GstQuery *query) DECLSPEC_HIDDEN;
+GstBusSyncReply watch_decoder_bus_wrapper(GstBus *bus, GstMessage *message, gpointer user) DECLSPEC_HIDDEN;
+GstFlowReturn decoder_new_sample_wrapper(GstElement *appsink, gpointer user) DECLSPEC_HIDDEN;
+void decoder_pad_added_wrapper(GstElement *element, GstPad *Pad, gpointer user) DECLSPEC_HIDDEN;
 
 #endif
diff --git a/dlls/winegstreamer/gst_private.h b/dlls/winegstreamer/gst_private.h
index 258df0d040..6e25b895ff 100644
--- a/dlls/winegstreamer/gst_private.h
+++ b/dlls/winegstreamer/gst_private.h
@@ -53,6 +53,7 @@ BOOL init_gstreamer(void) DECLSPEC_HIDDEN;
 
 void start_dispatch_thread(void) DECLSPEC_HIDDEN;
 
+extern HRESULT mfplat_DllRegisterServer(void) DECLSPEC_HIDDEN;
 extern HRESULT mfplat_get_class_object(REFCLSID rclsid, REFIID riid, void **obj) DECLSPEC_HIDDEN;
 
 GstCaps *make_mf_compatible_caps(GstCaps *caps);
@@ -61,6 +62,12 @@ GstCaps *caps_from_mf_media_type(IMFMediaType *type);
 IMFSample *mf_sample_from_gst_buffer(GstBuffer *in);
 GstBuffer *gst_buffer_from_mf_sample(IMFSample *in);
 
+enum decoder_type
+{
+    DECODER_TYPE_H264,
+    DECODER_TYPE_AAC,
+};
+HRESULT generic_decoder_construct(REFIID riid, void **obj, enum decoder_type);
 enum source_type
 {
     SOURCE_TYPE_MPEG_4,
diff --git a/dlls/winegstreamer/main.c b/dlls/winegstreamer/main.c
index 2872710b3e..12ca11fa6c 100644
--- a/dlls/winegstreamer/main.c
+++ b/dlls/winegstreamer/main.c
@@ -365,7 +365,8 @@ HRESULT WINAPI DllRegisterServer(void)
     IFilterMapper2_RegisterFilter(mapper, &CLSID_WAVEParser, wave_parserW, NULL, NULL, NULL, &reg_wave_parser);
 
     IFilterMapper2_Release(mapper);
-    return S_OK;
+
+    return mfplat_DllRegisterServer();
 }
 
 HRESULT WINAPI DllUnregisterServer(void)
diff --git a/dlls/winegstreamer/mf_decode.c b/dlls/winegstreamer/mf_decode.c
new file mode 100644
index 0000000000..1ee28d996a
--- /dev/null
+++ b/dlls/winegstreamer/mf_decode.c
@@ -0,0 +1,1320 @@
+#include "config.h"
+
+#include <gst/gst.h>
+
+#include "gst_private.h"
+#include "gst_cbs.h"
+
+#include <stdarg.h>
+
+#define COBJMACROS
+#define NONAMELESSUNION
+
+#include "mfapi.h"
+#include "mferror.h"
+#include "mfobjects.h"
+#include "mftransform.h"
+
+#include "wine/debug.h"
+#include "wine/heap.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(mfplat);
+
+/* keep in sync with mfplat.c's mft registrations */
+
+const GUID *h264_input_types[] = {&MFVideoFormat_H264};
+const GUID *h264_output_types[] = {&MFVideoFormat_I420, &MFVideoFormat_IYUV, &MFVideoFormat_NV12, &MFVideoFormat_YUY2, &MFVideoFormat_YV12};
+
+const GUID *aac_input_types[] = {&MFAudioFormat_AAC};
+const GUID *aac_output_types[] = {&MFAudioFormat_Float};
+
+static struct decoder_desc
+{
+    const GUID *major_type;
+    const GUID **input_types;
+    unsigned int input_types_count;
+    const GUID **output_types;
+    unsigned int output_types_count;
+} decoder_descs[] =
+{
+    { /* DECODER_TYPE_H264 */
+        &MFMediaType_Video,
+        h264_input_types,
+        ARRAY_SIZE(h264_input_types),
+        h264_output_types,
+        ARRAY_SIZE(h264_output_types),
+    },
+    { /* DECODER_TYPE_AAC */
+        &MFMediaType_Audio,
+        aac_input_types,
+        ARRAY_SIZE(aac_input_types),
+        aac_output_types,
+        ARRAY_SIZE(aac_output_types),
+    }
+};
+
+struct mf_decoder
+{
+    IMFTransform IMFTransform_iface;
+    IMFAsyncCallback process_message_callback;
+    LONG refcount;
+    enum decoder_type type;
+    BOOL video;
+    IMFMediaType *input_type, *output_type;
+    BOOL valid_state;
+    GstBus *bus;
+    GstElement *container;
+    GstElement *parser, *decoder, *post_process_start, *videobox, *appsink;
+    GstPad *input_src, *their_sink;
+    unsigned int output_counter;
+    BOOL flushing, draining;
+    CRITICAL_SECTION state_cs;
+    CONDITION_VARIABLE state_cv;
+    DWORD message_queue;
+};
+
+static struct mf_decoder *impl_mf_decoder_from_IMFTransform(IMFTransform *iface)
+{
+    return CONTAINING_RECORD(iface, struct mf_decoder, IMFTransform_iface);
+}
+
+static struct mf_decoder *impl_from_message_callback_IMFAsyncCallback(IMFAsyncCallback *iface)
+{
+    return CONTAINING_RECORD(iface, struct mf_decoder, process_message_callback);
+}
+
+static HRESULT WINAPI mf_decoder_QueryInterface (IMFTransform *iface, REFIID riid, void **out)
+{
+    TRACE("%p, %s, %p.\n", iface, debugstr_guid(riid), out);
+
+    if (IsEqualIID(riid, &IID_IMFTransform) ||
+            IsEqualIID(riid, &IID_IUnknown))
+    {
+        *out = iface;
+        IMFTransform_AddRef(iface);
+        return S_OK;
+    }
+
+    WARN("Unsupported %s.\n", debugstr_guid(riid));
+    *out = NULL;
+    return E_NOINTERFACE;
+}
+
+static ULONG WINAPI mf_decoder_AddRef(IMFTransform *iface)
+{
+    struct mf_decoder *decoder = impl_mf_decoder_from_IMFTransform(iface);
+    ULONG refcount = InterlockedIncrement(&decoder->refcount);
+
+    TRACE("%p, refcount %u.\n", iface, refcount);
+
+    return refcount;
+}
+
+static void mf_decoder_destroy(struct mf_decoder *decoder);
+static ULONG WINAPI mf_decoder_Release(IMFTransform *iface)
+{
+    struct mf_decoder *decoder = impl_mf_decoder_from_IMFTransform(iface);
+    ULONG refcount = InterlockedDecrement(&decoder->refcount);
+
+    TRACE("%p, refcount %u.\n", iface, refcount);
+
+    if (!refcount)
+    {
+        mf_decoder_destroy(decoder);
+    }
+
+    return refcount;
+}
+
+static HRESULT WINAPI mf_decoder_GetStreamLimits(IMFTransform *iface, DWORD *input_minimum, DWORD *input_maximum,
+        DWORD *output_minimum, DWORD *output_maximum)
+{
+    TRACE("%p, %p, %p, %p, %p.\n", iface, input_minimum, input_maximum, output_minimum, output_maximum);
+
+    *input_minimum = *input_maximum = *output_minimum = *output_maximum = 1;
+
+    return S_OK;
+}
+
+static HRESULT WINAPI mf_decoder_GetStreamCount(IMFTransform *iface, DWORD *inputs, DWORD *outputs)
+{
+    TRACE("%p %p %p.\n", iface, inputs, outputs);
+
+    *inputs = *outputs = 1;
+
+    return S_OK;
+}
+
+static HRESULT WINAPI mf_decoder_GetStreamIDs(IMFTransform *iface, DWORD input_size, DWORD *inputs,
+        DWORD output_size, DWORD *outputs)
+{
+    TRACE("%p %u %p %u %p.\n", iface, input_size, inputs, output_size, outputs);
+
+    return E_NOTIMPL;
+}
+
+
+static HRESULT WINAPI mf_decoder_GetInputStreamInfo(IMFTransform *iface, DWORD id, MFT_INPUT_STREAM_INFO *info)
+{
+    struct mf_decoder *decoder = impl_mf_decoder_from_IMFTransform(iface);
+
+    TRACE("%p %u %p\n", decoder, id, info);
+
+    if (id != 0)
+        return MF_E_INVALIDSTREAMNUMBER;
+
+    /* If we create a wrapped GstBuffer, remove MFT_INPUT_STREAM_DOES_NOT_ADDREF */
+    info->dwFlags = MFT_INPUT_STREAM_WHOLE_SAMPLES | MFT_INPUT_STREAM_DOES_NOT_ADDREF;
+    info->cbMaxLookahead = 0;
+    info->cbAlignment = 0;
+    /* this is incorrect */
+    info->hnsMaxLatency = 0;
+    return S_OK;
+}
+
+static HRESULT WINAPI mf_decoder_GetOutputStreamInfo(IMFTransform *iface, DWORD id, MFT_OUTPUT_STREAM_INFO *info)
+{
+    struct mf_decoder *decoder = impl_mf_decoder_from_IMFTransform(iface);
+    MFT_OUTPUT_STREAM_INFO stream_info = {};
+
+    TRACE("%p %u %p\n", decoder, id, info);
+
+    if (id != 0)
+        return MF_E_INVALIDSTREAMNUMBER;
+
+    stream_info.dwFlags = MFT_OUTPUT_STREAM_PROVIDES_SAMPLES;
+    stream_info.cbSize = 0;
+    stream_info.cbAlignment = 0;
+
+    *info = stream_info;
+
+    return S_OK;
+}
+
+static HRESULT WINAPI mf_decoder_GetAttributes(IMFTransform *iface, IMFAttributes **attributes)
+{
+    FIXME("%p, %p. semi-stub!\n", iface, attributes);
+
+    return MFCreateAttributes(attributes, 0);
+}
+
+static HRESULT WINAPI mf_decoder_GetInputStreamAttributes(IMFTransform *iface, DWORD id,
+        IMFAttributes **attributes)
+{
+    FIXME("%p, %u, %p. stub!\n", iface, id, attributes);
+
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI mf_decoder_GetOutputStreamAttributes(IMFTransform *iface, DWORD id,
+        IMFAttributes **attributes)
+{
+    FIXME("%p, %u, %p. stub!\n", iface, id, attributes);
+
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI mf_decoder_DeleteInputStream(IMFTransform *iface, DWORD id)
+{
+    FIXME("%p, %u. stub!\n", iface, id);
+
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI mf_decoder_AddInputStreams(IMFTransform *iface, DWORD streams, DWORD *ids)
+{
+    FIXME("%p, %u, %p. stub!\n", iface, streams, ids);
+
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI mf_decoder_GetInputAvailableType(IMFTransform *iface, DWORD id, DWORD index,
+        IMFMediaType **type)
+{
+    struct mf_decoder *decoder = impl_mf_decoder_from_IMFTransform(iface);
+    IMFMediaType *input_type;
+    HRESULT hr;
+
+    TRACE("%p, %u, %u, %p\n", decoder, id, index, type);
+
+    if (id != 0)
+        return MF_E_INVALIDSTREAMNUMBER;
+
+    if (index >= decoder_descs[decoder->type].input_types_count)
+        return MF_E_NO_MORE_TYPES;
+
+    if (FAILED(hr = MFCreateMediaType(&input_type)))
+        return hr;
+
+    if (FAILED(hr = IMFMediaType_SetGUID(input_type, &MF_MT_MAJOR_TYPE, decoder_descs[decoder->type].major_type)))
+    {
+        IMFMediaType_Release(input_type);
+        return hr;
+    }
+
+    if (FAILED(hr = IMFMediaType_SetGUID(input_type, &MF_MT_SUBTYPE, decoder_descs[decoder->type].input_types[index])))
+    {
+        IMFMediaType_Release(input_type);
+        return hr;
+    }
+
+    *type = input_type;
+
+    return S_OK;
+}
+
+static void copy_attr(IMFMediaType *target, IMFMediaType *source, const GUID *key)
+{
+    PROPVARIANT val;
+
+    if (SUCCEEDED(IMFAttributes_GetItem((IMFAttributes *)source, key, &val)))
+    {
+        IMFAttributes_SetItem((IMFAttributes* )target, key, &val);
+    }
+}
+
+static HRESULT WINAPI mf_decoder_GetOutputAvailableType(IMFTransform *iface, DWORD id, DWORD index,
+        IMFMediaType **type)
+{
+    struct mf_decoder *decoder = impl_mf_decoder_from_IMFTransform(iface);
+    IMFMediaType *output_type;
+    HRESULT hr;
+
+    TRACE("%p, %u, %u, %p\n", decoder, id, index, type);
+
+    if (id != 0)
+        return MF_E_INVALIDSTREAMNUMBER;
+
+    if (!(decoder->input_type))
+        return MF_E_TRANSFORM_TYPE_NOT_SET;
+
+    if (index >= decoder_descs[decoder->type].output_types_count)
+        return MF_E_NO_MORE_TYPES;
+
+    if (FAILED(hr = MFCreateMediaType(&output_type)))
+        return hr;
+
+    copy_attr(output_type, decoder->input_type, &MF_MT_FRAME_SIZE);
+    copy_attr(output_type, decoder->input_type, &MF_MT_FRAME_RATE);
+    copy_attr(output_type, decoder->input_type, &MF_MT_AUDIO_NUM_CHANNELS);
+    copy_attr(output_type, decoder->input_type, &MF_MT_AUDIO_SAMPLES_PER_SECOND);
+
+    if (FAILED(hr = IMFMediaType_SetGUID(output_type, &MF_MT_MAJOR_TYPE, decoder_descs[decoder->type].major_type)))
+    {
+        IMFMediaType_Release(output_type);
+        return hr;
+    }
+
+    if (FAILED(hr = IMFMediaType_SetGUID(output_type, &MF_MT_SUBTYPE, decoder_descs[decoder->type].output_types[index])))
+    {
+        IMFMediaType_Release(output_type);
+        return hr;
+    }
+
+    *type = output_type;
+
+    return S_OK;
+}
+
+static gboolean activate_push_mode(GstPad *pad, GstObject *parent, GstPadMode mode, gboolean activate)
+{
+    TRACE("%s mft input pad in %s mode.\n",
+            activate ? "Activating" : "Deactivating", gst_pad_mode_get_name(mode));
+
+    switch (mode) {
+        case GST_PAD_MODE_PUSH:
+            return TRUE;
+        default:
+            return FALSE;
+    }
+}
+
+static gboolean query_input_src(GstPad *pad, GstObject *parent, GstQuery *query)
+{
+    struct mf_decoder *decoder = gst_pad_get_element_private(pad);
+
+    TRACE("GStreamer queries MFT Input Pad %p for %s\n", decoder, GST_QUERY_TYPE_NAME(query));
+
+    switch (GST_QUERY_TYPE(query))
+    {
+        case GST_QUERY_CAPS:
+        {
+            gst_query_set_caps_result(query, caps_from_mf_media_type(decoder->input_type));
+            return TRUE;
+        }
+        case GST_QUERY_SCHEDULING:
+        {
+            gst_query_add_scheduling_mode(query, GST_PAD_MODE_PUSH);
+            return TRUE;
+        }
+        case GST_QUERY_SEEKING:
+        {
+            GstFormat format;
+            gboolean seekable;
+            gint64 segment_start, segment_end;
+
+            gst_query_parse_seeking(query, &format, &seekable, &segment_start, &segment_end);
+            gst_query_set_seeking(query, format, 0, segment_start, segment_end);
+            return TRUE;
+        }
+        case GST_QUERY_DURATION:
+        {
+            return FALSE;
+        }
+        case GST_QUERY_LATENCY:
+        {
+            return FALSE;
+        }
+        default:
+        {
+            ERR("Unhandled query type %s on MFT Input Pad %p\n", GST_QUERY_TYPE_NAME(query), decoder);
+            return gst_pad_query_default (pad, parent, query);
+        }
+    }
+}
+
+static GstFlowReturn decoder_new_sample(GstElement *appsink, gpointer user)
+{
+    struct mf_decoder *decoder = (struct mf_decoder *) user;
+
+    TRACE("new sample decoder=%p appsink=%p\n", decoder, appsink);
+
+    if (decoder->flushing)
+    {
+        GstSample *sample;
+        g_signal_emit_by_name(decoder->appsink, "pull-sample", &sample);
+        gst_sample_unref(sample);
+        return GST_FLOW_OK;
+    }
+
+    decoder->output_counter++;
+
+    return GST_FLOW_OK;
+}
+
+static BOOL find_decoder_from_caps(GstCaps *input_caps, GstElement **decoder, GstElement **parser)
+{
+    GList *parser_list_one, *parser_list_two;
+    GList *walk;
+    BOOL ret = TRUE;
+
+    TRACE("input caps: %s\n", gst_caps_to_string(input_caps));
+
+    parser_list_one = gst_element_factory_list_get_elements(GST_ELEMENT_FACTORY_TYPE_PARSER, 1);
+    parser_list_two = gst_element_factory_list_filter(parser_list_one, input_caps, GST_PAD_SINK, 0);
+    gst_plugin_feature_list_free(parser_list_one);
+    parser_list_one = parser_list_two;
+    if (!(g_list_length(parser_list_one)))
+    {
+        GList *decoder_list_one, *decoder_list_two;
+        decoder_list_one = gst_element_factory_list_get_elements(GST_ELEMENT_FACTORY_TYPE_DECODER, 1);
+        decoder_list_two = gst_element_factory_list_filter(decoder_list_one, input_caps, GST_PAD_SINK, 0);
+        gst_plugin_feature_list_free(decoder_list_one);
+        decoder_list_one = decoder_list_two;
+        if (!(g_list_length(decoder_list_one)) ||
+            !(*decoder = gst_element_factory_create(g_list_first(decoder_list_one)->data, NULL)))
+        {
+            gst_plugin_feature_list_free(decoder_list_one);
+            ERR("Failed to create decoder\n");
+            ret = FALSE;
+            goto done;
+        }
+        TRACE("Found decoder %s\n", GST_ELEMENT_NAME(g_list_first(decoder_list_one)->data));
+    }
+    else
+    {
+        for (walk = (GList *) parser_list_one; walk; walk = g_list_next(walk))
+        {
+            GstElementFactory *parser_factory = walk->data;
+            const GList *templates, *walk_templ;
+
+            templates = gst_element_factory_get_static_pad_templates(parser_factory);
+
+            for (walk_templ = (GList *)templates; walk_templ; walk_templ = g_list_next(walk_templ))
+            {
+                GList *decoder_list_one, *decoder_list_two;
+                GstStaticPadTemplate *templ = walk_templ->data;
+                GstCaps *templ_caps;
+
+                if (templ->direction != GST_PAD_SRC)
+                    continue;
+
+                templ_caps = gst_static_pad_template_get_caps(templ);
+
+                TRACE("Matching parser src caps %s to decoder.\n", gst_caps_to_string(templ_caps));
+
+                decoder_list_one = gst_element_factory_list_get_elements(GST_ELEMENT_FACTORY_TYPE_DECODER, 1);
+                decoder_list_two = gst_element_factory_list_filter(decoder_list_one, templ_caps, GST_PAD_SINK, 0);
+                gst_plugin_feature_list_free(decoder_list_one);
+                decoder_list_one = decoder_list_two;
+                gst_caps_unref(templ_caps);
+
+                if (!(g_list_length(decoder_list_one)))
+                    continue;
+
+                if (!(*parser = gst_element_factory_create(parser_factory, NULL)))
+                {
+                    gst_plugin_feature_list_free(decoder_list_one);
+                    ERR("Failed to create parser\n");
+                    ret = FALSE;
+                    goto done;
+                }
+
+                if (!(*decoder = gst_element_factory_create(g_list_first(decoder_list_one)->data, NULL)))
+                {
+                    gst_plugin_feature_list_free(decoder_list_one);
+                    ERR("Failed to create decoder\n");
+                    ret = FALSE;
+                    goto done;
+                }
+
+                TRACE("Found decoder %s parser %s\n",
+                GST_ELEMENT_NAME(g_list_first(decoder_list_one)->data), GST_ELEMENT_NAME(parser_factory));
+                gst_plugin_feature_list_free(decoder_list_one);
+
+                goto done;
+            }
+        }
+    }
+    done:
+    gst_plugin_feature_list_free(parser_list_one);
+    return ret;
+}
+
+static void decoder_update_pipeline(struct mf_decoder *decoder)
+{
+    GstCaps *input_caps = NULL;
+    RECT target_size = {0};
+    MFVideoArea *aperture;
+    UINT32 aperture_size;
+    GstSegment *segment;
+
+    decoder->valid_state = FALSE;
+
+    /* tear down current pipeline */
+    gst_element_set_state(decoder->container, GST_STATE_READY);
+    if (gst_element_get_state(decoder->container, NULL, NULL, -1) == GST_STATE_CHANGE_FAILURE)
+    {
+        ERR("Failed to stop container\n");
+    }
+
+    g_object_set(decoder->appsink, "caps", gst_caps_new_empty(), NULL);
+
+    if (decoder->input_src)
+    {
+        gst_pad_unlink(decoder->input_src, decoder->their_sink);
+        gst_object_unref(G_OBJECT(decoder->input_src));
+        decoder->input_src = NULL;
+    }
+
+    if (decoder->their_sink)
+    {
+        gst_object_unref(G_OBJECT(decoder->their_sink));
+        decoder->their_sink = NULL;
+    }
+
+    if (decoder->parser)
+    {
+        gst_element_unlink(decoder->parser, decoder->decoder);
+        gst_bin_remove(GST_BIN(decoder->container), decoder->parser);
+        decoder->parser = NULL;
+    }
+    if (decoder->decoder)
+    {
+        gst_element_unlink(decoder->decoder, decoder->post_process_start);
+        gst_bin_remove(GST_BIN(decoder->container), decoder->decoder);
+        decoder->decoder = NULL;
+    }
+
+    /* we can only have a valid state if an input and output type is present */
+    if (!decoder->input_type || !decoder->output_type)
+        return;
+
+    /* We do leave a lot of unfreed objects here when we failure,
+       but it will be cleaned up on the next call */
+
+    input_caps = caps_from_mf_media_type(decoder->input_type);
+
+    if (!(decoder->input_src = gst_pad_new_from_template(gst_pad_template_new(
+        "mf_src",
+        GST_PAD_SRC,
+        GST_PAD_ALWAYS,
+        input_caps
+        ), "input_src")))
+    {
+        ERR("Failed to create input source\n");
+        goto done;
+    }
+
+    gst_pad_set_activatemode_function(decoder->input_src, activate_push_mode_wrapper);
+    gst_pad_set_query_function(decoder->input_src, query_input_src_wrapper);
+    gst_pad_set_element_private(decoder->input_src, decoder);
+
+    if (!(find_decoder_from_caps(input_caps, &decoder->decoder, &decoder->parser)))
+    {
+        goto done;
+    }
+
+    gst_bin_add(GST_BIN(decoder->container), decoder->decoder);
+    if (decoder->parser)
+    {
+        gst_bin_add(GST_BIN(decoder->container), decoder->parser);
+    }
+
+    if (!(decoder->their_sink = gst_element_get_static_pad(decoder->parser ? decoder->parser : decoder->decoder, "sink")))
+    {
+        goto done;
+    }
+
+    if (SUCCEEDED(IMFMediaType_GetAllocatedBlob(decoder->output_type, &MF_MT_MINIMUM_DISPLAY_APERTURE, (UINT8 **) &aperture, &aperture_size)))
+    {
+        UINT64 frame_size;
+
+        TRACE("x: %u %u/65536, y: %u %u/65536, area: %u x %u\n", aperture->OffsetX.value, aperture->OffsetX.fract,
+            aperture->OffsetY.value, aperture->OffsetY.fract, aperture->Area.cx, aperture->Area.cy);
+
+        if (SUCCEEDED(IMFMediaType_GetUINT64(decoder->output_type, &MF_MT_FRAME_SIZE, &frame_size)))
+        {
+            DWORD width = frame_size >> 32;
+            DWORD height = frame_size;
+
+            target_size.left = -aperture->OffsetX.value;
+            target_size.top = -aperture->OffsetY.value;
+            target_size.right = aperture->Area.cx - width;
+            target_size.bottom = aperture->Area.cy - height;
+        }
+        else
+            ERR("missing frame size\n");
+
+        CoTaskMemFree(aperture);
+    }
+
+    if (decoder->videobox)
+    {
+        g_object_set(decoder->videobox, "top", target_size.top, NULL);
+        g_object_set(decoder->videobox, "bottom", target_size.bottom, NULL);
+        g_object_set(decoder->videobox, "left", target_size.left, NULL);
+        g_object_set(decoder->videobox, "right", target_size.right, NULL);
+    }
+
+    g_object_set(decoder->appsink, "caps", caps_from_mf_media_type(decoder->output_type), NULL);
+
+    if (gst_pad_link(decoder->input_src, decoder->their_sink) != GST_PAD_LINK_OK)
+    {
+        ERR("Failed to link input source to decoder sink\n");
+        return;
+    }
+
+    if (decoder->parser && !(gst_element_link(decoder->parser, decoder->decoder)))
+    {
+        ERR("Failed to link parser to decoder\n");
+        goto done;
+    }
+
+    if (!(gst_element_link(decoder->decoder, decoder->post_process_start)))
+    {
+        ERR("Failed to link decoder to first element in post processing chain\n");
+        goto done;
+    }
+
+    gst_element_set_state(decoder->container, GST_STATE_PLAYING);
+
+    gst_pad_set_active(decoder->input_src, 1);
+    gst_pad_push_event(decoder->input_src, gst_event_new_stream_start("decoder-stream"));
+    gst_pad_push_event(decoder->input_src, gst_event_new_caps(caps_from_mf_media_type(decoder->input_type)));
+    segment = gst_segment_new();
+    gst_segment_init(segment, GST_FORMAT_DEFAULT);
+    gst_pad_push_event(decoder->input_src, gst_event_new_segment(segment));
+
+    gst_element_get_state(decoder->container, NULL, NULL, -1);
+
+    decoder->valid_state = TRUE;
+    done:
+    if (input_caps)
+        gst_caps_unref(input_caps);
+    return;
+}
+
+static HRESULT WINAPI mf_decoder_SetInputType(IMFTransform *iface, DWORD id, IMFMediaType *type, DWORD flags)
+{
+    struct mf_decoder *decoder = impl_mf_decoder_from_IMFTransform(iface);
+    HRESULT hr = S_OK;
+
+    TRACE("%p, %u, %p, %#x\n", decoder, id, type, flags);
+
+    if (id != 0)
+        return MF_E_INVALIDSTREAMNUMBER;
+
+    if (type)
+    {
+        GUID major_type, subtype;
+        UINT64 unused;
+
+        if (FAILED(hr = IMFMediaType_GetGUID(type, &MF_MT_MAJOR_TYPE, &major_type)))
+            return hr;
+        if (FAILED(hr = IMFMediaType_GetGUID(type, &MF_MT_SUBTYPE, &subtype)))
+            return hr;
+
+        if (!(IsEqualGUID(&major_type, decoder_descs[decoder->type].major_type)))
+            return MF_E_INVALIDTYPE;
+
+        if (decoder->video)
+        {
+            if (FAILED(hr = IMFMediaType_GetUINT64(type, &MF_MT_FRAME_SIZE, &unused)))
+                return hr;
+        }
+
+        for (unsigned int i = 0; i < decoder_descs[decoder->type].input_types_count; i++)
+        {
+            if (IsEqualGUID(&subtype, decoder_descs[decoder->type].input_types[i]))
+                break;
+            if (i == decoder_descs[decoder->type].input_types_count)
+                return MF_E_INVALIDTYPE;
+        }
+    }
+
+    if (flags & MFT_SET_TYPE_TEST_ONLY)
+    {
+        return S_OK;
+    }
+
+    EnterCriticalSection(&decoder->state_cs);
+
+    if (type)
+    {
+        if (!decoder->input_type)
+            if (FAILED(hr = MFCreateMediaType(&decoder->input_type)))
+                goto done;
+
+        if (FAILED(hr = IMFMediaType_CopyAllItems(type, (IMFAttributes*) decoder->input_type)))
+            goto done;
+    }
+    else if (decoder->input_type)
+    {
+        IMFMediaType_Release(decoder->input_type);
+        decoder->input_type = NULL;
+    }
+
+    decoder_update_pipeline(decoder);
+
+    done:
+    LeaveCriticalSection(&decoder->state_cs);
+    WakeAllConditionVariable(&decoder->state_cv);
+    return hr;
+}
+
+static HRESULT WINAPI mf_decoder_SetOutputType(IMFTransform *iface, DWORD id, IMFMediaType *type, DWORD flags)
+{
+    struct mf_decoder *decoder = impl_mf_decoder_from_IMFTransform(iface);
+    HRESULT hr = S_OK;
+
+    TRACE("%p, %u, %p, %#x\n", decoder, id, type, flags);
+
+    if (id != 0)
+        return MF_E_INVALIDSTREAMNUMBER;
+
+    if (type)
+    {
+        GUID major_type, subtype;
+        UINT64 unused;
+
+        /* validate the type */
+        if (FAILED(hr = IMFMediaType_GetGUID(type, &MF_MT_MAJOR_TYPE, &major_type)))
+            return MF_E_INVALIDTYPE;
+        if (FAILED(hr = IMFMediaType_GetGUID(type, &MF_MT_SUBTYPE, &subtype)))
+            return MF_E_INVALIDTYPE;
+
+        if (!(IsEqualGUID(&major_type, decoder_descs[decoder->type].major_type)))
+            return MF_E_INVALIDTYPE;
+
+        if (decoder->video)
+        {
+            if (FAILED(hr = IMFMediaType_GetUINT64(type, &MF_MT_FRAME_SIZE, &unused)))
+                return MF_E_INVALIDTYPE;
+        }
+
+        for (unsigned int i = 0; i < decoder_descs[decoder->type].output_types_count; i++)
+        {
+            if (IsEqualGUID(&subtype, decoder_descs[decoder->type].output_types[i]))
+                break;
+            if (i == decoder_descs[decoder->type].output_types_count)
+                return MF_E_INVALIDTYPE;
+        }
+    }
+
+    if (flags & MFT_SET_TYPE_TEST_ONLY)
+    {
+        return S_OK;
+    }
+
+    EnterCriticalSection(&decoder->state_cs);
+    if (type)
+    {
+        if (!decoder->output_type)
+            if (FAILED(hr = MFCreateMediaType(&decoder->output_type)))
+                goto done;
+
+        if (FAILED(hr = IMFMediaType_CopyAllItems(type, (IMFAttributes*) decoder->output_type)))
+            goto done;
+    }
+    else if (decoder->output_type)
+    {
+        IMFMediaType_Release(decoder->output_type);
+        decoder->output_type = NULL;
+    }
+
+    decoder_update_pipeline(decoder);
+
+    done:
+    LeaveCriticalSection(&decoder->state_cs);
+    WakeAllConditionVariable(&decoder->state_cv);
+    return hr;
+}
+
+static HRESULT WINAPI mf_decoder_GetInputCurrentType(IMFTransform *iface, DWORD id, IMFMediaType **type)
+{
+    FIXME("%p, %u, %p. stub!\n", iface, id, type);
+
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI mf_decoder_GetOutputCurrentType(IMFTransform *iface, DWORD id, IMFMediaType **type)
+{
+    FIXME("%p, %u, %p. stub!\n", iface, id, type);
+
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI mf_decoder_GetInputStatus(IMFTransform *iface, DWORD id, DWORD *flags)
+{
+    struct mf_decoder *decoder = impl_mf_decoder_from_IMFTransform(iface);
+
+    TRACE("%p, %u, %p\n", decoder, id, flags);
+
+    *flags = decoder->output_counter ? MFT_INPUT_STATUS_ACCEPT_DATA : 0;
+
+    return S_OK;
+}
+
+static HRESULT WINAPI mf_decoder_GetOutputStatus(IMFTransform *iface, DWORD *flags)
+{
+    struct mf_decoder *decoder = impl_mf_decoder_from_IMFTransform(iface);
+
+    TRACE("%p, %p.\n", decoder, flags);
+
+    *flags = decoder->output_counter ? MFT_OUTPUT_STATUS_SAMPLE_READY : 0;
+
+    return S_OK;
+}
+
+static HRESULT WINAPI mf_decoder_SetOutputBounds(IMFTransform *iface, LONGLONG lower, LONGLONG upper)
+{
+    FIXME("%p, %s, %s. stub!\n", iface, wine_dbgstr_longlong(lower), wine_dbgstr_longlong(upper));
+
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI mf_decoder_ProcessEvent(IMFTransform *iface, DWORD id, IMFMediaEvent *event)
+{
+    FIXME("%p, %u, %p. stub!\n", iface, id, event);
+
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI decoder_process_message_callback_QueryInterface(IMFAsyncCallback *iface,
+        REFIID riid, void **obj)
+{
+    TRACE("%p, %s, %p.\n", iface, debugstr_guid(riid), obj);
+
+    if (IsEqualIID(riid, &IID_IMFAsyncCallback) ||
+            IsEqualIID(riid, &IID_IUnknown))
+    {
+        *obj = iface;
+        IMFAsyncCallback_AddRef(iface);
+        return S_OK;
+    }
+
+    WARN("Unsupported %s.\n", debugstr_guid(riid));
+    *obj = NULL;
+    return E_NOINTERFACE;
+}
+
+static ULONG WINAPI decoder_process_message_callback_AddRef(IMFAsyncCallback *iface)
+{
+    struct mf_decoder *decoder = impl_from_message_callback_IMFAsyncCallback(iface);
+    return IMFTransform_AddRef(&decoder->IMFTransform_iface);
+}
+
+static ULONG WINAPI decoder_process_message_callback_Release(IMFAsyncCallback *iface)
+{
+    struct mf_decoder *decoder = impl_from_message_callback_IMFAsyncCallback(iface);
+    return IMFTransform_Release(&decoder->IMFTransform_iface);
+}
+
+static HRESULT WINAPI decoder_process_message_callback_GetParameters(IMFAsyncCallback *iface,
+        DWORD *flags, DWORD *queue)
+{
+    return E_NOTIMPL;
+}
+
+const GUID WINE_MFT_MESSAGE_TYPE = {0xd09998bf, 0x102f, 0x4efa, {0x8f,0x84,0x06,0x1f,0xa4,0x10,0xf2,0x64}};
+
+static HRESULT WINAPI decoder_process_message_callback_Invoke(IMFAsyncCallback *iface, IMFAsyncResult *result)
+{
+    struct mf_decoder *decoder = impl_from_message_callback_IMFAsyncCallback(iface);
+    IUnknown *state;
+    IMFAttributes *async_param;
+    MFT_MESSAGE_TYPE message_type;
+    HRESULT hr;
+
+    state = IMFAsyncResult_GetStateNoAddRef(result);
+    if (!state)
+        return E_FAIL;
+    if (FAILED(hr = IUnknown_QueryInterface(state, &IID_IMFAttributes, (void **)&async_param)))
+        return hr;
+    if (FAILED(hr = IMFAttributes_GetUINT32(async_param, &WINE_MFT_MESSAGE_TYPE, &message_type)))
+    {
+        IMFAttributes_Release(async_param);
+        return hr;
+    }
+    IMFAttributes_Release(async_param);
+
+    switch (message_type)
+    {
+        case MFT_MESSAGE_COMMAND_DRAIN:
+        {
+            GstSegment *segment = gst_segment_new();
+            gst_segment_init(segment, GST_FORMAT_DEFAULT);
+
+            EnterCriticalSection(&decoder->state_cs);
+            decoder->draining = TRUE;
+            WakeAllConditionVariable(&decoder->state_cv);
+            LeaveCriticalSection(&decoder->state_cs);
+            gst_pad_push_event(decoder->input_src, gst_event_new_eos());
+
+            EnterCriticalSection(&decoder->state_cs);
+            while(decoder->draining)
+                SleepConditionVariableCS(&decoder->state_cv, &decoder->state_cs, INFINITE);
+            gst_pad_push_event(decoder->input_src, gst_event_new_flush_stop(0));
+            gst_pad_push_event(decoder->input_src, gst_event_new_segment(segment));
+            LeaveCriticalSection(&decoder->state_cs);
+            return S_OK;
+        }
+        default:
+            return E_FAIL;
+    }
+}
+
+static const IMFAsyncCallbackVtbl process_message_callback_vtbl =
+{
+    decoder_process_message_callback_QueryInterface,
+    decoder_process_message_callback_AddRef,
+    decoder_process_message_callback_Release,
+    decoder_process_message_callback_GetParameters,
+    decoder_process_message_callback_Invoke,
+};
+
+static HRESULT WINAPI mf_decoder_ProcessMessage(IMFTransform *iface, MFT_MESSAGE_TYPE message, ULONG_PTR param)
+{
+    struct mf_decoder *decoder = impl_mf_decoder_from_IMFTransform(iface);
+    IMFAttributes *async_param;
+    HRESULT hr;
+
+    TRACE("%p, %x %lu.\n", decoder, message, param);
+
+    if (FAILED(hr = MFCreateAttributes(&async_param, 1)))
+        return hr;
+
+    switch (message)
+    {
+        case MFT_MESSAGE_COMMAND_FLUSH:
+        {
+            GstSegment *segment = gst_segment_new();
+            gst_segment_init(segment, GST_FORMAT_DEFAULT);
+
+            EnterCriticalSection(&decoder->state_cs);
+            decoder->flushing = TRUE;
+
+            while (decoder->output_counter)
+            {
+                GstSample *sample;
+                g_signal_emit_by_name(decoder->appsink, "pull-sample", &sample);
+                gst_sample_unref(sample);
+                decoder->output_counter--;
+            }
+
+            gst_pad_push_event(decoder->input_src, gst_event_new_flush_start());
+            gst_pad_push_event(decoder->input_src, gst_event_new_flush_stop(0));
+            gst_pad_push_event(decoder->input_src, gst_event_new_segment(segment));
+            gst_element_set_state(decoder->container, GST_STATE_PLAYING);
+
+            decoder->flushing = FALSE;
+            LeaveCriticalSection(&decoder->state_cs);
+
+            hr = S_OK;
+            break;
+        }
+        case MFT_MESSAGE_COMMAND_DRAIN:
+        {
+            if (decoder->draining)
+            {
+                hr = S_OK;
+                break;
+            }
+
+            IMFAttributes_SetUINT32(async_param, &WINE_MFT_MESSAGE_TYPE, message);
+
+            EnterCriticalSection(&decoder->state_cs);
+            MFPutWorkItem(decoder->message_queue, &decoder->process_message_callback, (IUnknown *)async_param);
+            while (!decoder->draining)
+                SleepConditionVariableCS(&decoder->state_cv, &decoder->state_cs, INFINITE);
+            LeaveCriticalSection(&decoder->state_cs);
+
+            hr = S_OK;
+            break;
+        }
+        case MFT_MESSAGE_NOTIFY_BEGIN_STREAMING:
+        {
+            hr = S_OK;
+            break;
+        }
+        default:
+        {
+            ERR("Unhandled message type %x.\n", message);
+            hr = E_FAIL;
+            break;
+        }
+    }
+
+    IMFAttributes_Release(async_param);
+    return hr;
+}
+
+static HRESULT WINAPI mf_decoder_ProcessInput(IMFTransform *iface, DWORD id, IMFSample *sample, DWORD flags)
+{
+    struct mf_decoder *decoder = impl_mf_decoder_from_IMFTransform(iface);
+    GstBuffer *gst_buffer;
+    GstFlowReturn ret;
+    HRESULT hr = S_OK;
+    GstQuery *drain;
+
+    TRACE("%p, %u, %p, %#x\n", decoder, id, sample, flags);
+
+    if (flags)
+        WARN("Unsupported flags %#x\n", flags);
+
+    if (id != 0)
+        return MF_E_INVALIDSTREAMNUMBER;
+
+    if (!decoder->valid_state)
+        return MF_E_TRANSFORM_TYPE_NOT_SET;
+
+    EnterCriticalSection(&decoder->state_cs);
+
+    drain = gst_query_new_drain();
+    gst_pad_peer_query(decoder->input_src, drain);
+
+    if (decoder->output_counter || decoder->draining)
+    {
+        hr = MF_E_NOTACCEPTING;
+        goto done;
+    }
+
+    if (!(gst_buffer = gst_buffer_from_mf_sample(sample)))
+    {
+        hr = E_FAIL;
+        goto done;
+    }
+
+    ret = gst_pad_push(decoder->input_src, gst_buffer);
+    if (ret != GST_FLOW_OK)
+    {
+        ERR("Couldn't process input ret = %d\n", ret);
+        hr =  E_FAIL;
+        goto done;
+    }
+
+    done:
+    LeaveCriticalSection(&decoder->state_cs);
+    return hr;
+}
+
+static HRESULT WINAPI mf_decoder_ProcessOutput(IMFTransform *iface, DWORD flags, DWORD count,
+        MFT_OUTPUT_DATA_BUFFER *samples, DWORD *status)
+{
+    struct mf_decoder *decoder = impl_mf_decoder_from_IMFTransform(iface);
+    MFT_OUTPUT_DATA_BUFFER *relevant_buffer = NULL;
+    GstSample *buffer;
+
+    TRACE("%p, %#x, %u, %p, %p,\n", iface, flags, count, samples, status);
+
+    if (flags)
+        WARN("Unsupported flags %#x\n", flags);
+
+    if (!decoder->valid_state)
+        return MF_E_TRANSFORM_TYPE_NOT_SET;
+
+    for (unsigned int i = 0; i < count; i++)
+    {
+        MFT_OUTPUT_DATA_BUFFER *out_buffer = &samples[i];
+
+        if (out_buffer->dwStreamID != 0)
+            return MF_E_INVALIDSTREAMNUMBER;
+
+        if (relevant_buffer)
+            return MF_E_INVALIDSTREAMNUMBER;
+
+        relevant_buffer = out_buffer;
+    }
+
+    if (!relevant_buffer)
+        return S_OK;
+
+    EnterCriticalSection(&decoder->state_cs);
+
+    if (!decoder->output_counter && !decoder->draining)
+    {
+        LeaveCriticalSection(&decoder->state_cs);
+        return MF_E_TRANSFORM_NEED_MORE_INPUT;
+    }
+    TRACE("%u\n", decoder->output_counter);
+
+    g_signal_emit_by_name(decoder->appsink, "pull-sample", &buffer);
+    if (decoder->draining && !buffer)
+    {
+        decoder->output_counter = 0;
+        decoder->draining = FALSE;
+        LeaveCriticalSection(&decoder->state_cs);
+        WakeAllConditionVariable(&decoder->state_cv);
+        return MF_E_TRANSFORM_NEED_MORE_INPUT;
+    }
+    decoder->output_counter--;
+
+    LeaveCriticalSection(&decoder->state_cs);
+
+    relevant_buffer->pSample = mf_sample_from_gst_buffer(gst_sample_get_buffer(buffer));
+    gst_sample_unref(buffer);
+    relevant_buffer->dwStatus = S_OK;
+    relevant_buffer->pEvents = NULL;
+    *status = 0;
+    return S_OK;
+}
+
+static const IMFTransformVtbl mf_decoder_vtbl =
+{
+    mf_decoder_QueryInterface,
+    mf_decoder_AddRef,
+    mf_decoder_Release,
+    mf_decoder_GetStreamLimits,
+    mf_decoder_GetStreamCount,
+    mf_decoder_GetStreamIDs,
+    mf_decoder_GetInputStreamInfo,
+    mf_decoder_GetOutputStreamInfo,
+    mf_decoder_GetAttributes,
+    mf_decoder_GetInputStreamAttributes,
+    mf_decoder_GetOutputStreamAttributes,
+    mf_decoder_DeleteInputStream,
+    mf_decoder_AddInputStreams,
+    mf_decoder_GetInputAvailableType,
+    mf_decoder_GetOutputAvailableType,
+    mf_decoder_SetInputType,
+    mf_decoder_SetOutputType,
+    mf_decoder_GetInputCurrentType,
+    mf_decoder_GetOutputCurrentType,
+    mf_decoder_GetInputStatus,
+    mf_decoder_GetOutputStatus,
+    mf_decoder_SetOutputBounds,
+    mf_decoder_ProcessEvent,
+    mf_decoder_ProcessMessage,
+    mf_decoder_ProcessInput,
+    mf_decoder_ProcessOutput,
+};
+
+GstBusSyncReply watch_decoder_bus(GstBus *bus, GstMessage *message, gpointer user_data)
+{
+    struct mf_decoder *decoder = user_data;
+    GError *err = NULL;
+    gchar *dbg_info = NULL;
+
+    TRACE("decoder %p message type %s\n", decoder, GST_MESSAGE_TYPE_NAME(message));
+
+    switch (message->type)
+    {
+        case GST_MESSAGE_ERROR:
+            gst_message_parse_error(message, &err, &dbg_info);
+            ERR("%s: %s\n", GST_OBJECT_NAME(message->src), err->message);
+            ERR("%s\n", dbg_info);
+            g_error_free(err);
+            g_free(dbg_info);
+            break;
+        case GST_MESSAGE_WARNING:
+            gst_message_parse_warning(message, &err, &dbg_info);
+            WARN("%s: %s\n", GST_OBJECT_NAME(message->src), err->message);
+            WARN("%s\n", dbg_info);
+            g_error_free(err);
+            g_free(dbg_info);
+            break;
+        case GST_MESSAGE_EOS:
+            break;
+        default:
+            break;
+    }
+
+    return GST_BUS_DROP;
+}
+
+static void mf_decoder_destroy(struct mf_decoder *decoder)
+{
+    if (decoder->input_type)
+    {
+        IMFMediaType_Release(decoder->input_type);
+        decoder->input_type = NULL;
+    }
+
+    if (decoder->output_type)
+    {
+        IMFMediaType_Release(decoder->output_type);
+        decoder->output_type = NULL;
+    }
+
+    decoder_update_pipeline(decoder);
+
+    if (decoder->their_sink)
+        gst_object_unref(G_OBJECT(decoder->their_sink));
+
+    if (decoder->container)
+        gst_object_unref(G_OBJECT(decoder->container));
+
+    if (decoder->bus)
+        gst_object_unref(G_OBJECT(decoder->bus));
+
+    DeleteCriticalSection(&decoder->state_cs);
+
+    MFUnlockWorkQueue(decoder->message_queue);
+
+    heap_free(decoder);
+}
+
+HRESULT generic_decoder_construct(REFIID riid, void **obj, enum decoder_type type)
+{
+    struct mf_decoder *object;
+    GstElement *converter;
+    HRESULT hr = S_OK;
+
+    TRACE("%s, %p %u.\n", debugstr_guid(riid), obj, type);
+
+    if (!(object = heap_alloc_zero(sizeof(*object))))
+        return E_OUTOFMEMORY;
+    object->type = type;
+    object->video = decoder_descs[type].major_type == &MFMediaType_Video;
+    MFAllocateWorkQueue(&object->message_queue);
+
+    InitializeCriticalSection(&object->state_cs);
+    InitializeConditionVariable(&object->state_cv);
+
+    object->container = gst_bin_new(NULL);
+    object->bus = gst_bus_new();
+    gst_bus_set_sync_handler(object->bus, watch_decoder_bus_wrapper, object, NULL);
+    gst_element_set_bus(object->container, object->bus);
+
+    if (object->video)
+    {
+        if (!(object->videobox = gst_element_factory_make("videobox", NULL)))
+        {
+            ERR("Failed to create videobox\n");
+            hr = E_FAIL;
+            goto fail;
+        }
+        gst_bin_add(GST_BIN(object->container), object->videobox);
+    }
+
+    if (!(converter = gst_element_factory_make(object->video ? "videoconvert" : "audioconvert", NULL)))
+    {
+        ERR("Failed to create converter\n");
+        hr = E_FAIL;
+        goto fail;
+    }
+    gst_bin_add(GST_BIN(object->container), converter);
+
+    if (!(object->appsink = gst_element_factory_make("appsink", NULL)))
+    {
+        ERR("Failed to create appsink\n");
+        hr = E_FAIL;
+        goto fail;
+    }
+    gst_bin_add(GST_BIN(object->container), object->appsink);
+
+    g_object_set(object->appsink, "emit-signals", TRUE, NULL);
+    g_object_set(object->appsink, "sync", FALSE, NULL);
+    g_object_set(object->appsink, "async", FALSE, NULL);
+    g_signal_connect(object->appsink, "new-sample", G_CALLBACK(decoder_new_sample_wrapper), object);
+
+
+    if (object->videobox)
+    {
+        if (!(gst_element_link(object->videobox, converter)))
+        {
+            ERR("Failed to link videobox to converter\n");
+            hr = E_FAIL;
+            goto fail;
+        }
+    }
+
+    if (!(gst_element_link(converter, object->appsink)))
+    {
+        ERR("Failed to link converter appsink");
+        hr = E_FAIL;
+        goto fail;
+    }
+
+    object->post_process_start = object->videobox ? object->videobox : converter;
+
+    object->process_message_callback.lpVtbl = &process_message_callback_vtbl;
+
+    object->IMFTransform_iface.lpVtbl = &mf_decoder_vtbl;
+    object->refcount = 1;
+
+    *obj = object;
+    return S_OK;
+
+    fail:
+    ERR("Failed to create Decoder MFT type %u, hr = %#x\n", type, hr);
+    mf_decoder_destroy(object);
+    return hr;
+}
+
+void perform_cb_mf_decode(struct cb_data *cbdata)
+{
+    switch (cbdata->type)
+    {
+    case ACTIVATE_PUSH_MODE:
+        {
+            struct activate_mode_data *data = &cbdata->u.activate_mode_data;
+            cbdata->u.activate_mode_data.ret = activate_push_mode(data->pad, data->parent, data->mode, data->activate);
+            break;
+        }
+    case QUERY_INPUT_SRC:
+        {
+            struct query_function_data *data = &cbdata->u.query_function_data;
+            cbdata->u.query_function_data.ret = query_input_src(data->pad, data->parent, data->query);
+            break;
+        }
+    case DECODER_NEW_SAMPLE:
+        {
+            struct new_sample_data *data = &cbdata->u.new_sample_data;
+            cbdata->u.new_sample_data.ret = decoder_new_sample(data->appsink, data->user);
+            break;
+        }
+    case WATCH_DECODER_BUS:
+        {
+            struct watch_bus_data *data = &cbdata->u.watch_bus_data;
+            cbdata->u.watch_bus_data.ret = watch_decoder_bus(data->bus, data->msg, data->user);
+            break;
+        }
+    default:
+        {
+            ERR("Wrong callback forwarder called\n");
+            return;
+        }
+    }
+}
\ No newline at end of file
diff --git a/dlls/winegstreamer/mfplat.c b/dlls/winegstreamer/mfplat.c
index f1c8e34cd2..aa17b335b3 100644
--- a/dlls/winegstreamer/mfplat.c
+++ b/dlls/winegstreamer/mfplat.c
@@ -404,6 +404,16 @@ failed:
     return hr;
 }
 
+static HRESULT h264_decoder_create(REFIID riid, void **ret)
+{
+    return generic_decoder_construct(riid, ret, DECODER_TYPE_H264);
+}
+
+static HRESULT aac_decoder_create(REFIID riid, void **ret)
+{
+    return generic_decoder_construct(riid, ret, DECODER_TYPE_AAC);
+}
+
 static HRESULT mp4_stream_handler_create(REFIID riid, void **ret)
 {
     return container_stream_handler_construct(riid, ret, SOURCE_TYPE_MPEG_4);
@@ -417,6 +427,8 @@ static const struct class_object
 class_objects[] =
 {
     { &CLSID_VideoProcessorMFT, &video_processor_create },
+    { &CLSID_CMSH264DecoderMFT, &h264_decoder_create },
+    { &CLSID_CMSAACDecMFT, &aac_decoder_create },
     { &CLSID_MPEG4ByteStreamHandler, &mp4_stream_handler_create },
 };
 
@@ -449,6 +461,111 @@ HRESULT mfplat_get_class_object(REFCLSID rclsid, REFIID riid, void **obj)
     return CLASS_E_CLASSNOTAVAILABLE;
 }
 
+
+static WCHAR h264decoderW[] = {'H','.','2','6','4',' ','D','e','c','o','d','e','r',0};
+const GUID *h264_decoder_input_types[] =
+{
+    &MFVideoFormat_H264,
+};
+const GUID *h264_decoder_output_types[] =
+{
+    &MFVideoFormat_I420,
+    &MFVideoFormat_IYUV,
+    &MFVideoFormat_NV12,
+    &MFVideoFormat_YUY2,
+    &MFVideoFormat_YV12,
+};
+
+static WCHAR aacdecoderW[] = {'A','A','C',' ','D','e','c','o','d','e','r',0};
+const GUID *aac_decoder_input_types[] =
+{
+    &MFAudioFormat_AAC,
+};
+
+const GUID *aac_decoder_output_types[] =
+{
+    &MFAudioFormat_Float,
+    &MFAudioFormat_PCM,
+};
+
+static const struct mft
+{
+    const GUID *clsid;
+    const GUID *category;
+    LPWSTR name;
+    const UINT32 flags;
+    const GUID *major_type;
+    const UINT32 input_types_count;
+    const GUID **input_types;
+    const UINT32 output_types_count;
+    const GUID **output_types;
+    IMFAttributes *attributes;
+}
+mfts[] =
+{
+    {
+        &CLSID_CMSH264DecoderMFT,
+        &MFT_CATEGORY_VIDEO_DECODER,
+        h264decoderW,
+        MFT_ENUM_FLAG_SYNCMFT,
+        &MFMediaType_Video,
+        ARRAY_SIZE(h264_decoder_input_types),
+        h264_decoder_input_types,
+        ARRAY_SIZE(h264_decoder_output_types),
+        h264_decoder_output_types,
+        NULL
+    },
+    {
+        &CLSID_CMSAACDecMFT,
+        &MFT_CATEGORY_AUDIO_DECODER,
+        aacdecoderW,
+        MFT_ENUM_FLAG_SYNCMFT,
+        &MFMediaType_Audio,
+        ARRAY_SIZE(aac_decoder_input_types),
+        aac_decoder_input_types,
+        ARRAY_SIZE(aac_decoder_output_types),
+        aac_decoder_output_types,
+        NULL
+    }
+};
+
+HRESULT mfplat_DllRegisterServer(void)
+{
+    HRESULT hr;
+
+    for (unsigned int i = 0; i < ARRAY_SIZE(mfts); i++)
+    {
+        const struct mft *cur = &mfts[i];
+
+        MFT_REGISTER_TYPE_INFO *input_types, *output_types;
+        input_types = heap_alloc(cur->input_types_count * sizeof(input_types[0]));
+        output_types = heap_alloc(cur->output_types_count * sizeof(output_types[0]));
+        for (unsigned int i = 0; i < cur->input_types_count; i++)
+        {
+            input_types[i].guidMajorType = *(cur->major_type);
+            input_types[i].guidSubtype = *(cur->input_types[i]);
+        }
+        for (unsigned int i = 0; i < cur->output_types_count; i++)
+        {
+            output_types[i].guidMajorType = *(cur->major_type);
+            output_types[i].guidSubtype = *(cur->output_types[i]);
+        }
+
+        hr = MFTRegister(*(cur->clsid), *(cur->category), cur->name, cur->flags, cur->input_types_count,
+                    input_types, cur->output_types_count, output_types, cur->attributes);
+
+        heap_free(input_types);
+        heap_free(output_types);
+
+        if (FAILED(hr))
+        {
+            FIXME("Failed to register MFT, hr %#x\n", hr);
+            return hr;
+        }
+    }
+    return S_OK;
+}
+
 const static struct
 {
     const GUID *subtype;
@@ -467,7 +584,6 @@ struct aac_user_data
 {
     WORD payload_type;
     WORD profile_level_indication;
-    WORD struct_type;
     WORD reserved;
   /* audio-specific-config is stored here */
 };
diff --git a/dlls/winegstreamer/winegstreamer_classes.idl b/dlls/winegstreamer/winegstreamer_classes.idl
index 997a28b052..ee8090cf4a 100644
--- a/dlls/winegstreamer/winegstreamer_classes.idl
+++ b/dlls/winegstreamer/winegstreamer_classes.idl
@@ -61,3 +61,15 @@ coclass VideoProcessorMFT {}
     uuid(271c3902-6095-4c45-a22f-20091816ee9e)
 ]
 coclass MPEG4ByteStreamHandler {}
+
+[
+    threading(both),
+    uuid(62ce7e72-4c71-4d20-b15d-452831a87d9d)
+]
+coclass CMSH264DecoderMFT { }
+
+[
+    threading(both),
+    uuid(32d186a7-218f-4c75-8876-dd77273a8999)
+]
+coclass CMSAACDecMFT { }
diff --git a/include/mfidl.idl b/include/mfidl.idl
index c61ccb788c..8e198594c5 100644
--- a/include/mfidl.idl
+++ b/include/mfidl.idl
@@ -1128,4 +1128,6 @@ cpp_quote("EXTERN_GUID(MF_AUDIO_RENDERER_ATTRIBUTE_ENDPOINT_ROLE, 0x6ba644ff, 0x
 cpp_quote("EXTERN_GUID(MF_AUDIO_RENDERER_ATTRIBUTE_STREAM_CATEGORY, 0xa9770471, 0x92ec, 0x4df4, 0x94, 0xfe, 0x81, 0xc3, 0x6f, 0x0c, 0x3a, 0x7a);")
 
 cpp_quote("EXTERN_GUID(CLSID_VideoProcessorMFT, 0x88753b26, 0x5b24, 0x49bd, 0xb2, 0xe7, 0xc, 0x44, 0x5c, 0x78, 0xc9, 0x82);")
-cpp_quote("EXTERN_GUID(CLSID_MPEG4ByteStreamHandler, 0x271c3902, 0x6095, 0x4c45, 0xa2, 0x2f, 0x20, 0x09, 0x18, 0x16, 0xee, 0x9e);")
\ No newline at end of file
+cpp_quote("EXTERN_GUID(CLSID_MPEG4ByteStreamHandler, 0x271c3902, 0x6095, 0x4c45, 0xa2, 0x2f, 0x20, 0x09, 0x18, 0x16, 0xee, 0x9e);")
+cpp_quote("EXTERN_GUID(CLSID_CMSH264DecoderMFT, 0x62ce7e72, 0x4c71, 0x4d20, 0xb1, 0x5d, 0x45, 0x28, 0x31, 0xa8, 0x7d, 0x9d);")
+cpp_quote("EXTERN_GUID(CLSID_CMSAACDecMFT, 0x32d186a7, 0x218f, 0x4c75, 0x88, 0x76, 0xdd, 0x77, 0x27, 0x3a, 0x89, 0x99);")
-- 
2.25.3

From 8850496bb733ba14f23d455ba27f25bd7690e291 Mon Sep 17 00:00:00 2001
From: Derek Lesho <dlesho@codeweavers.com>
Date: Mon, 23 Mar 2020 11:55:41 -0500
Subject: [PATCH 29/46] mfreadwrite: Select all streams when creating a source
 reader.

---
 dlls/mfreadwrite/reader.c | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/dlls/mfreadwrite/reader.c b/dlls/mfreadwrite/reader.c
index eaf52ce31e..a7291ff1ae 100644
--- a/dlls/mfreadwrite/reader.c
+++ b/dlls/mfreadwrite/reader.c
@@ -2094,6 +2094,10 @@ static HRESULT create_source_reader_from_source(IMFMediaSource *source, IMFAttri
             break;
 
         object->streams[i].index = i;
+
+        hr = IMFPresentationDescriptor_SelectStream(object->descriptor, i);
+        if (FAILED(hr))
+            break;
     }
 
     if (FAILED(hr))
-- 
2.25.3

From e039458f6a57481d4508d1a589b61f4d433446f1 Mon Sep 17 00:00:00 2001
From: Derek Lesho <dlesho@codeweavers.com>
Date: Mon, 16 Mar 2020 15:27:27 -0500
Subject: [PATCH 30/46] Miscellaneous

---
 dlls/mfreadwrite/reader.c         |  12 +++-
 dlls/winegstreamer/gst_cbs.c      |   9 +++
 dlls/winegstreamer/media_source.c | 115 +++++++++++++++++++++++++++++-
 3 files changed, 133 insertions(+), 3 deletions(-)

diff --git a/dlls/mfreadwrite/reader.c b/dlls/mfreadwrite/reader.c
index a7291ff1ae..25b0050256 100644
--- a/dlls/mfreadwrite/reader.c
+++ b/dlls/mfreadwrite/reader.c
@@ -1570,6 +1570,7 @@ static HRESULT source_reader_create_decoder_for_stream(struct source_reader *rea
 {
     MFT_REGISTER_TYPE_INFO in_type, out_type;
     CLSID *clsids, mft_clsid, category;
+    BOOL decoder_found = FALSE;
     unsigned int i = 0, count;
     IMFMediaType *input_type;
     HRESULT hr;
@@ -1616,12 +1617,21 @@ static HRESULT source_reader_create_decoder_for_stream(struct source_reader *rea
                 }
 
             }
+            else if (!decoder_found)
+            {
+                /* see if there are other decoders for this stream */
+                if (SUCCEEDED(MFTEnum(category, 0, &in_type, NULL, NULL, &clsids, &count)) && count)
+                {
+                    decoder_found = TRUE;
+                    CoTaskMemFree(clsids);
+                }
+            }
         }
 
         IMFMediaType_Release(input_type);
     }
 
-    return MF_E_TOPO_CODEC_NOT_FOUND;
+    return decoder_found ? MF_E_INVALIDREQUEST : MF_E_TOPO_CODEC_NOT_FOUND;
 }
 
 static HRESULT WINAPI src_reader_SetCurrentMediaType(IMFSourceReader *iface, DWORD index, DWORD *reserved,
diff --git a/dlls/winegstreamer/gst_cbs.c b/dlls/winegstreamer/gst_cbs.c
index 64dddaf47c..098ad4b09d 100644
--- a/dlls/winegstreamer/gst_cbs.c
+++ b/dlls/winegstreamer/gst_cbs.c
@@ -18,6 +18,9 @@
 
 #include "config.h"
 
+#include <stdio.h>
+#include <assert.h>
+
 #include <gst/gst.h>
 
 #include "objbase.h"
@@ -53,6 +56,12 @@ static void CALLBACK perform_cb(TP_CALLBACK_INSTANCE *instance, void *user)
         perform_cb_media_source(cbdata);
     else if (cbdata->type < MF_DECODE_MAX)
         perform_cb_mf_decode(cbdata);
+    else
+    {
+        fprintf(stderr, "No handler registered for callback\n");
+        assert(0);
+    }
+
 
     pthread_mutex_lock(&cbdata->lock);
     cbdata->finished = 1;
diff --git a/dlls/winegstreamer/media_source.c b/dlls/winegstreamer/media_source.c
index ebb88d2b8a..8e204e983c 100644
--- a/dlls/winegstreamer/media_source.c
+++ b/dlls/winegstreamer/media_source.c
@@ -84,6 +84,8 @@ struct source_async_command
 struct media_source
 {
     IMFMediaSource IMFMediaSource_iface;
+    IMFGetService IMFGetService_iface;
+    IMFSeekInfo IMFSeekInfo_iface;
     IMFAsyncCallback async_commands_callback;
     LONG ref;
     DWORD async_commands_queue;
@@ -118,6 +120,16 @@ static inline struct media_source *impl_from_IMFMediaSource(IMFMediaSource *ifac
     return CONTAINING_RECORD(iface, struct media_source, IMFMediaSource_iface);
 }
 
+static inline struct media_source *impl_from_IMFGetService(IMFGetService *iface)
+{
+    return CONTAINING_RECORD(iface, struct media_source, IMFGetService_iface);
+}
+
+static inline struct media_source *impl_from_IMFSeekInfo(IMFSeekInfo *iface)
+{
+    return CONTAINING_RECORD(iface, struct media_source, IMFSeekInfo_iface);
+}
+
 static inline struct media_source *impl_from_async_commands_callback_IMFAsyncCallback(IMFAsyncCallback *iface)
 {
     return CONTAINING_RECORD(iface, struct media_source, async_commands_callback);
@@ -1071,6 +1083,10 @@ static HRESULT WINAPI media_source_QueryInterface(IMFMediaSource *iface, REFIID
     {
         *out = &source->IMFMediaSource_iface;
     }
+    else if(IsEqualIID(riid, &IID_IMFGetService))
+    {
+        *out = &source->IMFGetService_iface;
+    }
     else
     {
         FIXME("(%s, %p)\n", debugstr_guid(riid), out);
@@ -1165,7 +1181,7 @@ static HRESULT WINAPI media_source_GetCharacteristics(IMFMediaSource *iface, DWO
     if (source->state == SOURCE_SHUTDOWN)
         return MF_E_SHUTDOWN;
 
-    *characteristics = MFMEDIASOURCE_CAN_SEEK;
+    *characteristics = MFMEDIASOURCE_CAN_SEEK | MFMEDIASOURCE_CAN_PAUSE;
 
     return S_OK;
 }
@@ -1305,6 +1321,99 @@ static const IMFMediaSourceVtbl IMFMediaSource_vtbl =
     media_source_Shutdown,
 };
 
+static HRESULT WINAPI source_get_service_QueryInterface(IMFGetService *iface, REFIID riid, void **obj)
+{
+    struct media_source *source = impl_from_IMFGetService(iface);
+    return IMFMediaSource_QueryInterface(&source->IMFMediaSource_iface, riid, obj);
+}
+
+static ULONG WINAPI source_get_service_AddRef(IMFGetService *iface)
+{
+    struct media_source *source = impl_from_IMFGetService(iface);
+    return IMFMediaSource_AddRef(&source->IMFMediaSource_iface);
+}
+
+static ULONG WINAPI source_get_service_Release(IMFGetService *iface)
+{
+    struct media_source *source = impl_from_IMFGetService(iface);
+    return IMFMediaSource_Release(&source->IMFMediaSource_iface);
+}
+
+static HRESULT WINAPI source_get_service_GetService(IMFGetService *iface, REFGUID service, REFIID riid, void **obj)
+{
+    struct media_source *source = impl_from_IMFGetService(iface);
+
+    TRACE("(%p)->(%s, %s, %p)\n", source, debugstr_guid(service), debugstr_guid(riid), obj);
+
+    if (source->state == SOURCE_SHUTDOWN)
+        return MF_E_SHUTDOWN;
+
+    *obj = NULL;
+
+    if (IsEqualIID(service, &MF_SCRUBBING_SERVICE))
+    {
+        if (IsEqualIID(riid, &IID_IMFSeekInfo))
+        {
+            *obj = &source->IMFSeekInfo_iface;
+        }
+    }
+
+    if (*obj)
+        IUnknown_AddRef((IUnknown*) *obj);
+
+    return *obj ? S_OK : E_NOINTERFACE;
+}
+
+static const IMFGetServiceVtbl IMFGetService_vtbl =
+{
+    source_get_service_QueryInterface,
+    source_get_service_AddRef,
+    source_get_service_Release,
+    source_get_service_GetService,
+};
+
+static HRESULT WINAPI source_seek_info_QueryInterface(IMFSeekInfo *iface, REFIID riid, void **obj)
+{
+    struct media_source *source = impl_from_IMFSeekInfo(iface);
+    return IMFMediaSource_QueryInterface(&source->IMFMediaSource_iface, riid, obj);
+}
+
+static ULONG WINAPI source_seek_info_AddRef(IMFSeekInfo *iface)
+{
+    struct media_source *source = impl_from_IMFSeekInfo(iface);
+    return IMFMediaSource_AddRef(&source->IMFMediaSource_iface);
+}
+
+static ULONG WINAPI source_seek_info_Release(IMFSeekInfo *iface)
+{
+    struct media_source *source = impl_from_IMFSeekInfo(iface);
+    return IMFMediaSource_Release(&source->IMFMediaSource_iface);
+}
+
+static HRESULT WINAPI source_seek_info_GetNearestKeyFrames(IMFSeekInfo *iface, const GUID *format,
+        const PROPVARIANT *position, PROPVARIANT *prev_frame, PROPVARIANT *next_frame)
+{
+    struct media_source *source = impl_from_IMFSeekInfo(iface);
+
+    FIXME("(%p)->(%s, %p, %p, %p) - semi-stub\n", source, debugstr_guid(format), position, prev_frame, next_frame);
+
+    if (source->state == SOURCE_SHUTDOWN)
+        return MF_E_SHUTDOWN;
+
+    PropVariantCopy(prev_frame, position);
+    PropVariantCopy(next_frame, position);
+
+    return S_OK;
+}
+
+static const IMFSeekInfoVtbl IMFSeekInfo_vtbl =
+{
+    source_seek_info_QueryInterface,
+    source_seek_info_AddRef,
+    source_seek_info_Release,
+    source_seek_info_GetNearestKeyFrames,
+};
+
 static HRESULT media_source_constructor(IMFByteStream *bytestream, enum source_type type, struct media_source **out_media_source)
 {
     GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE(
@@ -1324,6 +1433,8 @@ static HRESULT media_source_constructor(IMFByteStream *bytestream, enum source_t
         return E_OUTOFMEMORY;
 
     object->IMFMediaSource_iface.lpVtbl = &IMFMediaSource_vtbl;
+    object->IMFGetService_iface.lpVtbl = &IMFGetService_vtbl;
+    object->IMFSeekInfo_iface.lpVtbl = &IMFSeekInfo_vtbl;
     object->async_commands_callback.lpVtbl = &source_async_commands_callback_vtbl;
     object->ref = 1;
     object->type = type;
@@ -1423,7 +1534,6 @@ static HRESULT media_source_constructor(IMFByteStream *bytestream, enum source_t
     heap_free(descriptors);
     descriptors = NULL;
 
-    /* miscelaneous presentation descriptor setup */
     {
         IMFAttributes *byte_stream_attributes;
         gint64 total_pres_time = 0;
@@ -1440,6 +1550,7 @@ static HRESULT media_source_constructor(IMFByteStream *bytestream, enum source_t
             IMFAttributes_Release(byte_stream_attributes);
         }
 
+        /* TODO: consider streams which don't start at T=0 */
         for (unsigned int i = 0; i < object->stream_count; i++)
         {
             GstQuery *query = gst_query_new_duration(GST_FORMAT_TIME);
-- 
2.25.3

From ad2a26f1462fc801fed606404ea794ebf6d431fe Mon Sep 17 00:00:00 2001
From: Derek Lesho <dlesho@codeweavers.com>
Date: Wed, 25 Mar 2020 19:07:11 -0500
Subject: [PATCH 31/46] WMV

---
 dlls/winegstreamer/gst_private.h             |  2 +
 dlls/winegstreamer/media_source.c            |  3 ++
 dlls/winegstreamer/mf_decode.c               | 10 +++++
 dlls/winegstreamer/mfplat.c                  | 47 ++++++++++++++++++++
 dlls/winegstreamer/winegstreamer.rgs         | 12 +++++
 dlls/winegstreamer/winegstreamer_classes.idl | 12 +++++
 include/mfidl.idl                            |  2 +
 7 files changed, 88 insertions(+)

diff --git a/dlls/winegstreamer/gst_private.h b/dlls/winegstreamer/gst_private.h
index 6e25b895ff..0b61bc9e3c 100644
--- a/dlls/winegstreamer/gst_private.h
+++ b/dlls/winegstreamer/gst_private.h
@@ -66,11 +66,13 @@ enum decoder_type
 {
     DECODER_TYPE_H264,
     DECODER_TYPE_AAC,
+    DECODER_TYPE_WMV,
 };
 HRESULT generic_decoder_construct(REFIID riid, void **obj, enum decoder_type);
 enum source_type
 {
     SOURCE_TYPE_MPEG_4,
+    SOURCE_TYPE_ASF,
 };
 HRESULT container_stream_handler_construct(REFIID riid, void **obj, enum source_type);
 
diff --git a/dlls/winegstreamer/media_source.c b/dlls/winegstreamer/media_source.c
index 8e204e983c..19b5b4346b 100644
--- a/dlls/winegstreamer/media_source.c
+++ b/dlls/winegstreamer/media_source.c
@@ -30,6 +30,9 @@ static struct source_desc
 {
     {/*SOURCE_TYPE_MPEG_4*/
         GST_STATIC_CAPS("video/quicktime"),
+    },
+    {/*SOURCE_TYPE_ASF*/
+        GST_STATIC_CAPS("video/x-ms-asf"),
     }
 };
 
diff --git a/dlls/winegstreamer/mf_decode.c b/dlls/winegstreamer/mf_decode.c
index 1ee28d996a..618467a124 100644
--- a/dlls/winegstreamer/mf_decode.c
+++ b/dlls/winegstreamer/mf_decode.c
@@ -28,6 +28,9 @@ const GUID *h264_output_types[] = {&MFVideoFormat_I420, &MFVideoFormat_IYUV, &MF
 const GUID *aac_input_types[] = {&MFAudioFormat_AAC};
 const GUID *aac_output_types[] = {&MFAudioFormat_Float};
 
+const GUID *wmv_input_types[] = {&MFVideoFormat_WMV3, &MFVideoFormat_WVC1};
+const GUID *wmv_output_types[] = {&MFVideoFormat_NV12, &MFVideoFormat_YV12, &MFVideoFormat_YUY2, &MFVideoFormat_UYVY, &MFVideoFormat_YVYU, &MFVideoFormat_NV11, &MFVideoFormat_RGB32, &MFVideoFormat_RGB24, &MFVideoFormat_RGB555, &MFVideoFormat_RGB8};
+
 static struct decoder_desc
 {
     const GUID *major_type;
@@ -50,6 +53,13 @@ static struct decoder_desc
         ARRAY_SIZE(aac_input_types),
         aac_output_types,
         ARRAY_SIZE(aac_output_types),
+    },
+    { /* DECODER_TYPE_WMV */
+        &MFMediaType_Video,
+        wmv_input_types,
+        ARRAY_SIZE(wmv_input_types),
+        wmv_output_types,
+        ARRAY_SIZE(wmv_output_types),
     }
 };
 
diff --git a/dlls/winegstreamer/mfplat.c b/dlls/winegstreamer/mfplat.c
index aa17b335b3..b9b23d55d9 100644
--- a/dlls/winegstreamer/mfplat.c
+++ b/dlls/winegstreamer/mfplat.c
@@ -414,11 +414,21 @@ static HRESULT aac_decoder_create(REFIID riid, void **ret)
     return generic_decoder_construct(riid, ret, DECODER_TYPE_AAC);
 }
 
+static HRESULT wmv_decoder_create(REFIID riid, void **ret)
+{
+    return generic_decoder_construct(riid, ret, DECODER_TYPE_WMV);
+}
+
 static HRESULT mp4_stream_handler_create(REFIID riid, void **ret)
 {
     return container_stream_handler_construct(riid, ret, SOURCE_TYPE_MPEG_4);
 }
 
+static HRESULT asf_stream_handler_create(REFIID riid, void **ret)
+{
+    return container_stream_handler_construct(riid, ret, SOURCE_TYPE_ASF);
+}
+
 static const struct class_object
 {
     const GUID *clsid;
@@ -429,7 +439,9 @@ class_objects[] =
     { &CLSID_VideoProcessorMFT, &video_processor_create },
     { &CLSID_CMSH264DecoderMFT, &h264_decoder_create },
     { &CLSID_CMSAACDecMFT, &aac_decoder_create },
+    { &CLSID_CWMVDecMediaObject, &wmv_decoder_create },
     { &CLSID_MPEG4ByteStreamHandler, &mp4_stream_handler_create },
+    { &CLSID_ASFByteStreamHandler, &asf_stream_handler_create },
 };
 
 HRESULT mfplat_get_class_object(REFCLSID rclsid, REFIID riid, void **obj)
@@ -488,6 +500,29 @@ const GUID *aac_decoder_output_types[] =
     &MFAudioFormat_PCM,
 };
 
+static WCHAR wmvdecoderW[] = {'W','M','V','i','d','e','o',' ','D','e','c','o','d','e','r',' ','M','F','T',0};
+
+const GUID *wmv_decoder_input_types[] =
+{
+    &MFVideoFormat_WMV3,
+    &MFVideoFormat_WVC1,
+};
+
+const GUID *wmv_decoder_output_types[] =
+{
+    &MFVideoFormat_NV12,
+    &MFVideoFormat_YV12,
+    &MFVideoFormat_YUY2,
+    &MFVideoFormat_UYVY,
+    &MFVideoFormat_YVYU,
+    &MFVideoFormat_NV11,
+    &MFVideoFormat_RGB32,
+    &MFVideoFormat_RGB24,
+    &MFVideoFormat_RGB565,
+    &MFVideoFormat_RGB555,
+    &MFVideoFormat_RGB8,
+};
+
 static const struct mft
 {
     const GUID *clsid;
@@ -526,6 +561,18 @@ mfts[] =
         ARRAY_SIZE(aac_decoder_output_types),
         aac_decoder_output_types,
         NULL
+    },
+    {
+        &CLSID_CWMVDecMediaObject,
+        &MFT_CATEGORY_VIDEO_DECODER,
+        wmvdecoderW,
+        MFT_ENUM_FLAG_SYNCMFT,
+        &MFMediaType_Video,
+        ARRAY_SIZE(wmv_decoder_input_types),
+        wmv_decoder_input_types,
+        ARRAY_SIZE(wmv_decoder_output_types),
+        wmv_decoder_output_types,
+        NULL
     }
 };
 
diff --git a/dlls/winegstreamer/winegstreamer.rgs b/dlls/winegstreamer/winegstreamer.rgs
index 0bc26761e9..9dbbca1c80 100644
--- a/dlls/winegstreamer/winegstreamer.rgs
+++ b/dlls/winegstreamer/winegstreamer.rgs
@@ -23,6 +23,10 @@ HKLM
             {
                 NoRemove 'ByteStreamHandlers'
                 {
+                    '.asf'
+                    {
+                        val '{41457294-644C-4298-A28A-BD69F2C0CF3B}' = s 'ASF Byte Stream Handler'
+                    }
                     '.m4v'
                     {
                         val '{271C3902-6095-4c45-A22F-20091816EE9E}' = s 'MPEG4 Byte Stream Handler'
@@ -31,6 +35,14 @@ HKLM
                     {
                         val '{271C3902-6095-4c45-A22F-20091816EE9E}' = s 'MPEG4 Byte Stream Handler'
                     }
+                    '.wma'
+                    {
+                        val '{41457294-644C-4298-A28A-BD69F2C0CF3B}' = s 'ASF Byte Stream Handler'
+                    }
+                    '.wmv'
+                    {
+                        val '{41457294-644C-4298-A28A-BD69F2C0CF3B}' = s 'ASF Byte Stream Handler'
+                    }
                     'video/m4v'
                     {
                         val '{271C3902-6095-4c45-A22F-20091816EE9E}' = s 'MPEG4 Byte Stream Handler'
diff --git a/dlls/winegstreamer/winegstreamer_classes.idl b/dlls/winegstreamer/winegstreamer_classes.idl
index ee8090cf4a..376e08c418 100644
--- a/dlls/winegstreamer/winegstreamer_classes.idl
+++ b/dlls/winegstreamer/winegstreamer_classes.idl
@@ -73,3 +73,15 @@ coclass CMSH264DecoderMFT { }
     uuid(32d186a7-218f-4c75-8876-dd77273a8999)
 ]
 coclass CMSAACDecMFT { }
+
+[
+    threading(both),
+    uuid(41457294-644c-4298-a28a-BD69F2C0CF3B)
+]
+coclass ASFByteStreamHandler {}
+
+[
+    threading(both),
+    uuid(82d353df-90bd-4382-8bc2-3f6192b76e34)
+]
+coclass CLSID_CWMVDecMediaObject {}
\ No newline at end of file
diff --git a/include/mfidl.idl b/include/mfidl.idl
index 8e198594c5..3f99485442 100644
--- a/include/mfidl.idl
+++ b/include/mfidl.idl
@@ -1131,3 +1131,5 @@ cpp_quote("EXTERN_GUID(CLSID_VideoProcessorMFT, 0x88753b26, 0x5b24, 0x49bd, 0xb2
 cpp_quote("EXTERN_GUID(CLSID_MPEG4ByteStreamHandler, 0x271c3902, 0x6095, 0x4c45, 0xa2, 0x2f, 0x20, 0x09, 0x18, 0x16, 0xee, 0x9e);")
 cpp_quote("EXTERN_GUID(CLSID_CMSH264DecoderMFT, 0x62ce7e72, 0x4c71, 0x4d20, 0xb1, 0x5d, 0x45, 0x28, 0x31, 0xa8, 0x7d, 0x9d);")
 cpp_quote("EXTERN_GUID(CLSID_CMSAACDecMFT, 0x32d186a7, 0x218f, 0x4c75, 0x88, 0x76, 0xdd, 0x77, 0x27, 0x3a, 0x89, 0x99);")
+cpp_quote("EXTERN_GUID(CLSID_ASFByteStreamHandler, 0x41457294, 0x644c, 0x4298, 0xa2, 0x8a, 0xbd, 0x69, 0xf2, 0xc0, 0xcf, 0x3b);")
+cpp_quote("EXTERN_GUID(CLSID_CWMVDecMediaObject, 0x82d353df, 0x90bd, 0x4382, 0x8b, 0xc2, 0x3f, 0x61, 0x92, 0xb7, 0x6e, 0x34);")
\ No newline at end of file
-- 
2.25.3

From 8d7098e42b90436b869165a6f9ad62db31d1a670 Mon Sep 17 00:00:00 2001
From: Derek Lesho <dlesho@codeweavers.com>
Date: Thu, 2 Apr 2020 15:42:18 -0500
Subject: [PATCH 32/46] mf: Ask for more samples from upstream node when upon
 MF_E_TRANSFORM_NEED_MORE_INPUT

---
 dlls/mf/session.c | 11 ++++++++++-
 1 file changed, 10 insertions(+), 1 deletion(-)

diff --git a/dlls/mf/session.c b/dlls/mf/session.c
index d6b881218b..68720247fc 100644
--- a/dlls/mf/session.c
+++ b/dlls/mf/session.c
@@ -2437,6 +2437,8 @@ static HRESULT transform_node_pull_samples(struct topo_node *node)
     return hr;
 }
 
+static HRESULT session_request_sample_from_node(struct media_session *session, IMFTopologyNode *node, DWORD output);
+
 static void session_deliver_sample_to_node(struct media_session *session, IMFTopologyNode *node, unsigned int input,
         IMFSample *sample)
 {
@@ -2509,7 +2511,14 @@ static void session_deliver_sample_to_node(struct media_session *session, IMFTop
                     WARN("Drain command failed for transform, hr %#x.\n", hr);
             }
 
-            transform_node_pull_samples(topo_node);
+            if (transform_node_pull_samples(topo_node) == MF_E_TRANSFORM_NEED_MORE_INPUT && !drain)
+            {
+                IMFTopologyNode *upstream_node;
+                DWORD upstream_output;
+
+                if (SUCCEEDED(IMFTopologyNode_GetInput(node, input, &upstream_node, &upstream_output)))
+                    session_request_sample_from_node(session, upstream_node, upstream_output);
+            }
 
             /* Remaining unprocessed input has been discarded, now queue markers for every output. */
             if (drain)
-- 
2.25.3

From 4099b2410c9f92de013030df2199d1e3865e5e71 Mon Sep 17 00:00:00 2001
From: Derek Lesho <dlesho@codeweavers.com>
Date: Thu, 2 Apr 2020 15:43:52 -0500
Subject: [PATCH 33/46] mf: Miscelaneous fixes to topology resolution.

---
 dlls/mf/topology.c | 16 +++++++++-------
 1 file changed, 9 insertions(+), 7 deletions(-)

diff --git a/dlls/mf/topology.c b/dlls/mf/topology.c
index 7a4c95bea0..ce706a9ff7 100644
--- a/dlls/mf/topology.c
+++ b/dlls/mf/topology.c
@@ -2006,7 +2006,7 @@ static HRESULT topology_loader_resolve_branch(IMFTopologyNode *src, IMFMediaType
                         IMFTopologyNode_ConnectOutput(src, 0, node_dec, 0);
                         IMFTopologyNode_ConnectOutput(node_dec, 0, sink, 0);
 
-                        IMFActivate_ShutdownObject(activates_convs[i]);
+                        IMFActivate_ShutdownObject(activates_decs[i]);
                         return S_OK;
                     }
 
@@ -2145,12 +2145,14 @@ static HRESULT topology_loader_resolve_partial_topology(struct topology_node *sr
     }
     else
     {
-        if (SUCCEEDED(topology_loader_resolve_branch(clone_src, src_mediatypes[0], clone_sink, MF_CONNECT_DIRECT)))
-        {
-            topology_loader_add_branch(*full_topology, clone_src, clone_sink);
-            heap_free(src_mediatypes);
-            return S_OK;
-        }
+        for (method = MF_CONNECT_DIRECT; method < MF_CONNECT_RESOLVE_INDEPENDENT_OUTPUTTYPES; method++)
+            if (SUCCEEDED(topology_loader_resolve_branch(clone_src, src_mediatypes[0], clone_sink, method)))
+            {
+                TRACE("Successfully connected nodes with method %u\n", method);
+                topology_loader_add_branch(*full_topology, clone_src, clone_sink);
+                heap_free(src_mediatypes);
+                return S_OK;
+            }
     }
 
     heap_free(src_mediatypes);
-- 
2.25.3

From 7f70e7e815c7e2ccacab8cb0e6a0f3fe8d163715 Mon Sep 17 00:00:00 2001
From: Derek Lesho <dlesho@codeweavers.com>
Date: Thu, 2 Apr 2020 15:45:52 -0500
Subject: [PATCH 34/46] Rewrite branch resolver.

and a HACK: Set output type of found decoder, this should probably happen somewhere else.
---
 dlls/mf/topology.c | 279 +++++++++++++++++++++++++++------------------
 1 file changed, 171 insertions(+), 108 deletions(-)

diff --git a/dlls/mf/topology.c b/dlls/mf/topology.c
index ce706a9ff7..337bf38081 100644
--- a/dlls/mf/topology.c
+++ b/dlls/mf/topology.c
@@ -1928,135 +1928,194 @@ static void topology_loader_add_branch(struct topology *topology, IMFTopologyNod
     }
 }
 
+struct available_output_type
+{
+    IMFMediaType *type;
+    IMFTransform *transform;
+};
+
+static HRESULT topology_loader_enumerate_output_types(GUID *category, IMFMediaType *input_type, HRESULT (*new_type)(struct available_output_type *, void *), void *context)
+{
+    MFT_REGISTER_TYPE_INFO mft_typeinfo;
+    GUID major_type, subtype;
+    IMFActivate **activates;
+    UINT32 num_activates;
+    HRESULT hr;
+
+    if (FAILED(hr = IMFMediaType_GetMajorType(input_type, &major_type)))
+        return hr;
+
+    if (FAILED(hr = IMFMediaType_GetGUID(input_type, &MF_MT_SUBTYPE, &subtype)))
+        return hr;
+
+    mft_typeinfo.guidMajorType = major_type;
+    mft_typeinfo.guidSubtype = subtype;
+
+    if (FAILED(hr = MFTEnumEx(*category, MFT_ENUM_FLAG_ALL, &mft_typeinfo, NULL, &activates, &num_activates)))
+        return hr;
+
+    hr = E_FAIL;
+
+    for (unsigned int i = 0; i < num_activates; i++)
+    {
+        IMFTransform *mft;
+
+        IMFActivate_ActivateObject(activates[i], &IID_IMFTransform, (void**) &mft);
+
+        if (SUCCEEDED(hr = IMFTransform_SetInputType(mft, 0, input_type, 0)))
+        {
+            struct available_output_type avail = {.transform = mft};
+            unsigned int output_count = 0;
+
+            while (SUCCEEDED(IMFTransform_GetOutputAvailableType(mft, 0, output_count++, &avail.type)))
+            {
+                if (SUCCEEDED(hr = new_type(&avail, context)))
+                {
+                    IMFActivate_ShutdownObject(activates[i]);
+                    return hr;
+                }
+            }
+        }
+
+        IMFActivate_ShutdownObject(activates[i]);
+    }
+
+    return hr;
+}
+
+struct connect_to_sink_context
+{
+    IMFTopologyNode *src, *sink;
+    IMFMediaTypeHandler *sink_mth;
+};
+
+HRESULT connect_to_sink(struct available_output_type *type, void *context)
+{
+    IMFTopologyNode *node;
+    struct connect_to_sink_context *ctx = context;
+
+    if (SUCCEEDED(IMFMediaTypeHandler_SetCurrentMediaType(ctx->sink_mth, type->type)))
+    {
+        MFCreateTopologyNode(MF_TOPOLOGY_TRANSFORM_NODE, &node);
+        IMFTopologyNode_SetObject(node, (IUnknown *) type->transform);
+        IMFTopologyNode_ConnectOutput(ctx->src, 0, node, 0);
+        IMFTopologyNode_ConnectOutput(node, 0, ctx->sink, 0);
+
+        IMFTransform_SetOutputType(type->transform, 0, type->type, 0);
+
+        return S_OK;
+    }
+    return E_FAIL;
+}
+
+struct connect_to_converter_context
+{
+    struct connect_to_sink_context sink_ctx;
+    GUID *converter_category;
+};
+
+HRESULT connect_to_converter(struct available_output_type *type, void *context)
+{
+    struct connect_to_converter_context *ctx = context;
+    struct connect_to_sink_context sink_ctx;
+    IMFTopologyNode *node;
+
+    MFCreateTopologyNode(MF_TOPOLOGY_TRANSFORM_NODE, &node);
+    IMFTopologyNode_SetObject(node, (IUnknown *) type->transform);
+
+    sink_ctx = ctx->sink_ctx;
+    sink_ctx.src = node;
+    if (SUCCEEDED(topology_loader_enumerate_output_types(ctx->converter_category, type->type, connect_to_sink, &sink_ctx)))
+    {
+        IMFTopologyNode_ConnectOutput(ctx->sink_ctx.src, 0, node, 0);
+
+        IMFTransform_SetOutputType(type->transform, 0, type->type, 0);
+
+        return S_OK;
+    }
+    IMFTopologyNode_Release(node);
+    return E_FAIL;
+}
+
 static HRESULT topology_loader_resolve_branch(IMFTopologyNode *src, IMFMediaType *mediatype, IMFTopologyNode *sink, MF_CONNECT_METHOD method)
 {
+    struct connect_to_converter_context convert_ctx;
+    struct connect_to_sink_context sink_ctx;
+    GUID major_type, decode_cat, convert_cat;
     IMFStreamSink *streamsink;
     IMFMediaTypeHandler *mth;
     HRESULT hr;
 
+    TRACE("method %u\n", method);
+
     IMFTopologyNode_GetObject(sink, (IUnknown **)&streamsink);
     IMFStreamSink_GetMediaTypeHandler(streamsink, &mth);
-    if (method == MF_CONNECT_DIRECT)
+
+    if (SUCCEEDED(hr = IMFMediaTypeHandler_SetCurrentMediaType(mth, mediatype)))
     {
-        if (FAILED(hr = IMFMediaTypeHandler_SetCurrentMediaType(mth, mediatype)))
-            return hr;
-        hr = IMFTopologyNode_ConnectOutput(src, 0, sink, 0);
+        return IMFTopologyNode_ConnectOutput(src, 0, sink, 0);
+    }
+
+    if (FAILED(hr = IMFMediaType_GetMajorType(mediatype, &major_type)))
         return hr;
+
+    if (IsEqualGUID(&major_type, &MFMediaType_Audio))
+    {
+        decode_cat = MFT_CATEGORY_AUDIO_DECODER;
+        convert_cat = MFT_CATEGORY_AUDIO_EFFECT;
     }
-    else
+    else if (IsEqualGUID(&major_type, &MFMediaType_Video))
     {
-        IMFTopologyNode *node_dec, *node_conv;
-        GUID major_type, subtype, mft_category;
-        MFT_REGISTER_TYPE_INFO mft_typeinfo;
-        UINT32 flags = MFT_ENUM_FLAG_ALL;
-        IMFActivate **activates_decs;
-        UINT32 num_activates_decs;
-        int i, j;
-
-        IMFMediaType_GetGUID(mediatype, &MF_MT_MAJOR_TYPE, &major_type);
-        if (IsEqualGUID(&major_type, &MFMediaType_Audio))
-            mft_category = MFT_CATEGORY_AUDIO_DECODER;
-        else if (IsEqualGUID(&major_type, &MFMediaType_Video))
-            mft_category = MFT_CATEGORY_VIDEO_DECODER;
-        else
-            return MF_E_INVALIDMEDIATYPE;
-
-        IMFMediaType_GetGUID(mediatype, &MF_MT_SUBTYPE, &subtype);
-        mft_typeinfo.guidMajorType = major_type;
-        mft_typeinfo.guidSubtype = subtype;
-        MFTEnumEx(mft_category, flags, &mft_typeinfo, NULL, &activates_decs, &num_activates_decs);
-
-        /* for getting converters later on */
-        if (IsEqualGUID(&major_type, &MFMediaType_Audio))
-            mft_category = MFT_CATEGORY_AUDIO_EFFECT;
-        else if (IsEqualGUID(&major_type, &MFMediaType_Video))
-            mft_category = MFT_CATEGORY_VIDEO_EFFECT;
-
-        /*
-         * Iterate over number of decoders.
-         * Try to set input type on decoder with source's output media type.
-         * If succeeds, iterate over decoder's output media types.
-         * Try to set input type on sink with decoder's output media type.
-         * If fails, iterate over number of converters.
-         * Try to set input type on converter with decoder's output media type.
-         * If succeeds, iterate over converters output media types.
-         * Try to set input type on sink with converter's output media type.
-         */
-        for (i = 0; i < num_activates_decs; i++)
-        {
-            IMFTransform *decoder;
+        decode_cat = MFT_CATEGORY_VIDEO_DECODER;
+        convert_cat = MFT_CATEGORY_VIDEO_EFFECT;
+    }
+    else
+        return E_FAIL;
 
-            IMFActivate_ActivateObject(activates_decs[i], &IID_IMFTransform, (void **)&decoder);
-            if (SUCCEEDED(hr = IMFTransform_SetInputType(decoder, 0, mediatype, 0)))
-            {
-                UINT32 num_activates_convs;
-                IMFActivate **activates_convs;
-                IMFMediaType *decoder_mtype;
+    sink_ctx.src = src;
+    sink_ctx.sink = sink;
+    sink_ctx.sink_mth = mth;
 
-                int count = 0;
-                while (SUCCEEDED(IMFTransform_GetOutputAvailableType(decoder, 0, count++, &decoder_mtype)))
-                {
-                    IMFTransform *converter;
+    convert_ctx.sink_ctx = sink_ctx;
+    convert_ctx.converter_category = &convert_cat;
 
-                    /* succeeded with source -> decoder -> sink */
-                    if (SUCCEEDED(IMFMediaTypeHandler_SetCurrentMediaType(mth, decoder_mtype)))
-                    {
-                        MFCreateTopologyNode(MF_TOPOLOGY_TRANSFORM_NODE, &node_dec);
-                        IMFTopologyNode_SetObject(node_dec, (IUnknown *)decoder);
-                        IMFTopologyNode_ConnectOutput(src, 0, node_dec, 0);
-                        IMFTopologyNode_ConnectOutput(node_dec, 0, sink, 0);
+    hr = E_FAIL;
 
-                        IMFActivate_ShutdownObject(activates_decs[i]);
-                        return S_OK;
-                    }
+    if (method & MF_CONNECT_ALLOW_CONVERTER)
+    {
+        if (SUCCEEDED(hr = topology_loader_enumerate_output_types(&convert_cat, mediatype, connect_to_sink, &sink_ctx)))
+            goto done;
+    }
 
-                    IMFMediaType_GetGUID(decoder_mtype, &MF_MT_SUBTYPE, &subtype);
-                    mft_typeinfo.guidSubtype = subtype;
-                    MFTEnumEx(mft_category, flags, &mft_typeinfo, NULL, &activates_convs, &num_activates_convs);
-                    for (j = 0; j < num_activates_convs; j++)
-                    {
-                        IMFMediaType *converter_mtype;
-
-                        IMFActivate_ActivateObject(activates_convs[j], &IID_IMFTransform, (void **)&converter);
-                        if (SUCCEEDED(IMFTransform_SetInputType(converter, 0, decoder_mtype, 0)))
-                        {
-                            int count = 0;
-                            while (SUCCEEDED(IMFTransform_GetOutputAvailableType(converter, 0, count++, &converter_mtype)))
-                            {
-                                /* succeeded with source -> decoder -> converter -> sink */
-                                if (SUCCEEDED(IMFMediaTypeHandler_SetCurrentMediaType(mth, converter_mtype)))
-                                {
-                                    MFCreateTopologyNode(MF_TOPOLOGY_TRANSFORM_NODE, &node_dec);
-                                    IMFTopologyNode_SetObject(node_dec, (IUnknown *)decoder);
-                                    MFCreateTopologyNode(MF_TOPOLOGY_TRANSFORM_NODE, &node_conv);
-                                    IMFTopologyNode_SetObject(node_conv, (IUnknown *)converter);
-                                    IMFTopologyNode_ConnectOutput(src, 0, node_dec, 0);
-                                    IMFTopologyNode_ConnectOutput(node_dec, 0, node_conv, 0);
-                                    IMFTopologyNode_ConnectOutput(node_conv, 0, sink, 0);
-
-                                    IMFActivate_ShutdownObject(activates_convs[j]);
-                                    IMFActivate_ShutdownObject(activates_decs[i]);
-                                    return S_OK;
-                                }
-                            }
-                        }
-                        IMFActivate_ShutdownObject(activates_convs[j]);
-                    }
-                }
-            }
-            IMFActivate_ShutdownObject(activates_decs[i]);
+    /* 2 = decoder flag */
+    if (method & 2)
+    {
+        if (method & MF_CONNECT_ALLOW_CONVERTER)
+        {
+            if (SUCCEEDED(hr = topology_loader_enumerate_output_types(&decode_cat, mediatype, connect_to_converter, &convert_ctx)))
+                goto done;
+        }
+        else
+        {
+            if (SUCCEEDED(hr = topology_loader_enumerate_output_types(&decode_cat, mediatype, connect_to_sink, &sink_ctx)))
+                goto done;
         }
     }
-    return E_FAIL;
+
+    done:
+    IMFMediaTypeHandler_Release(mth);
+    IMFStreamSink_Release(streamsink);
+    return hr;
 }
 
 static HRESULT topology_loader_resolve_partial_topology(struct topology_node *src, struct topology_node *sink, struct topology *topology, struct topology **full_topology)
 {
+    UINT32 method, src_method, sink_method, enum_src_types, streamid;
     IMFMediaTypeHandler *mth_src, *mth_sink;
     IMFTopologyNode *clone_src, *clone_sink;
-    UINT32 method, enum_src_types, streamid;
     IMFMediaType **src_mediatypes;
     IMFStreamDescriptor *desc;
+    IMFAttributes *attrs_sink;
     IMFAttributes *attrs_src;
     IMFStreamSink *strm_sink;
     IMFMediaType *mtype_src;
@@ -2064,6 +2123,7 @@ static HRESULT topology_loader_resolve_partial_topology(struct topology_node *sr
     HRESULT hr;
     int i;
 
+    attrs_sink = sink->attributes;
     attrs_src = src->attributes;
     if (FAILED(hr = IMFAttributes_GetUnknown(attrs_src, &MF_TOPONODE_STREAM_DESCRIPTOR, &IID_IMFStreamDescriptor, (void **)&desc)))
         return hr;
@@ -2117,12 +2177,15 @@ static HRESULT topology_loader_resolve_partial_topology(struct topology_node *sr
     if (FAILED(IMFTopologyNode_GetUINT32(clone_sink, &MF_TOPONODE_STREAMID, &streamid)))
         IMFTopologyNode_SetUINT32(clone_sink, &MF_TOPONODE_STREAMID, 0);
 
+    if (FAILED(IMFAttributes_GetUINT32(attrs_sink, &MF_TOPONODE_CONNECT_METHOD, &sink_method)))
+        sink_method = MF_CONNECT_ALLOW_DECODER;
+
     if (enum_src_types)
     {
-        hr = IMFAttributes_GetUINT32(attrs_src, &MF_TOPONODE_CONNECT_METHOD, &method);
-        if (hr == S_OK && method != MF_CONNECT_RESOLVE_INDEPENDENT_OUTPUTTYPES)
+        hr = IMFAttributes_GetUINT32(attrs_src, &MF_TOPONODE_CONNECT_METHOD, &src_method);
+        if (hr == S_OK && src_method != MF_CONNECT_RESOLVE_INDEPENDENT_OUTPUTTYPES)
         {
-            for (method = MF_CONNECT_DIRECT; method < MF_CONNECT_RESOLVE_INDEPENDENT_OUTPUTTYPES; method++)
+            for (method = MF_CONNECT_DIRECT; method <= sink_method; method++)
                 for (i = 0; i < num_media_types; i++)
                     if (SUCCEEDED(topology_loader_resolve_branch(clone_src, src_mediatypes[i], clone_sink, method)))
                     {
@@ -2134,7 +2197,7 @@ static HRESULT topology_loader_resolve_partial_topology(struct topology_node *sr
         else
         {
             for (i = 0; i < num_media_types; i++)
-                for (method = MF_CONNECT_DIRECT; method < MF_CONNECT_RESOLVE_INDEPENDENT_OUTPUTTYPES; method++)
+                for (method = MF_CONNECT_DIRECT; method <= sink_method; method++)
                     if (SUCCEEDED(topology_loader_resolve_branch(clone_src, src_mediatypes[i], clone_sink, method)))
                     {
                         topology_loader_add_branch(*full_topology, clone_src, clone_sink);
@@ -2145,10 +2208,10 @@ static HRESULT topology_loader_resolve_partial_topology(struct topology_node *sr
     }
     else
     {
-        for (method = MF_CONNECT_DIRECT; method < MF_CONNECT_RESOLVE_INDEPENDENT_OUTPUTTYPES; method++)
+        for (method = MF_CONNECT_DIRECT; method <= sink_method; method++)
             if (SUCCEEDED(topology_loader_resolve_branch(clone_src, src_mediatypes[0], clone_sink, method)))
             {
-                TRACE("Successfully connected nodes with method %u\n", method);
+                TRACE("Successfully connected nodes with method %u\n", src_method);
                 topology_loader_add_branch(*full_topology, clone_src, clone_sink);
                 heap_free(src_mediatypes);
                 return S_OK;
-- 
2.25.3

From 552916eaa4f87877ee4abd302843de64a1019806 Mon Sep 17 00:00:00 2001
From: Derek Lesho <dlesho@codeweavers.com>
Date: Thu, 2 Apr 2020 15:46:25 -0500
Subject: [PATCH 35/46] winegstreamer:  Implement
 IMFMedisStream::GetMediaSource.

---
 dlls/winegstreamer/media_source.c | 7 +++++--
 1 file changed, 5 insertions(+), 2 deletions(-)

diff --git a/dlls/winegstreamer/media_source.c b/dlls/winegstreamer/media_source.c
index 19b5b4346b..5c01346987 100644
--- a/dlls/winegstreamer/media_source.c
+++ b/dlls/winegstreamer/media_source.c
@@ -841,12 +841,15 @@ static HRESULT WINAPI media_stream_GetMediaSource(IMFMediaStream *iface, IMFMedi
 {
     struct media_stream *stream = impl_from_IMFMediaStream(iface);
 
-    FIXME("stub (%p)->(%p)\n", stream, source);
+    TRACE("(%p)->(%p)\n", stream, source);
 
     if (stream->state == STREAM_SHUTDOWN)
         return MF_E_SHUTDOWN;
 
-    return E_NOTIMPL;
+    IMFMediaSource_AddRef(&stream->parent_source->IMFMediaSource_iface);
+    *source = &stream->parent_source->IMFMediaSource_iface;
+
+    return S_OK;
 }
 
 static HRESULT WINAPI media_stream_GetStreamDescriptor(IMFMediaStream* iface, IMFStreamDescriptor **descriptor)
-- 
2.25.3

From 0ed26a07a48899700f02930de77ee3b0dd19bbc2 Mon Sep 17 00:00:00 2001
From: Derek Lesho <dlesho@codeweavers.com>
Date: Fri, 3 Apr 2020 11:12:33 -0500
Subject: [PATCH 36/46] Expose PCM output type on AAC decoder.

---
 dlls/winegstreamer/mf_decode.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/dlls/winegstreamer/mf_decode.c b/dlls/winegstreamer/mf_decode.c
index 618467a124..d44bdb5f4b 100644
--- a/dlls/winegstreamer/mf_decode.c
+++ b/dlls/winegstreamer/mf_decode.c
@@ -26,7 +26,7 @@ const GUID *h264_input_types[] = {&MFVideoFormat_H264};
 const GUID *h264_output_types[] = {&MFVideoFormat_I420, &MFVideoFormat_IYUV, &MFVideoFormat_NV12, &MFVideoFormat_YUY2, &MFVideoFormat_YV12};
 
 const GUID *aac_input_types[] = {&MFAudioFormat_AAC};
-const GUID *aac_output_types[] = {&MFAudioFormat_Float};
+const GUID *aac_output_types[] = {&MFAudioFormat_Float, &MFAudioFormat_PCM};
 
 const GUID *wmv_input_types[] = {&MFVideoFormat_WMV3, &MFVideoFormat_WVC1};
 const GUID *wmv_output_types[] = {&MFVideoFormat_NV12, &MFVideoFormat_YV12, &MFVideoFormat_YUY2, &MFVideoFormat_UYVY, &MFVideoFormat_YVYU, &MFVideoFormat_NV11, &MFVideoFormat_RGB32, &MFVideoFormat_RGB24, &MFVideoFormat_RGB555, &MFVideoFormat_RGB8};
-- 
2.25.3

From 33ab2b83d5e35875d43e31eda4ad87dc1fd89e9b Mon Sep 17 00:00:00 2001
From: Derek Lesho <dlesho@codeweavers.com>
Date: Fri, 3 Apr 2020 13:09:30 -0500
Subject: [PATCH 37/46] Set BPS on AAC.

---
 dlls/winegstreamer/mfplat.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/dlls/winegstreamer/mfplat.c b/dlls/winegstreamer/mfplat.c
index b9b23d55d9..b195bc0269 100644
--- a/dlls/winegstreamer/mfplat.c
+++ b/dlls/winegstreamer/mfplat.c
@@ -898,6 +898,7 @@ static IMFMediaType* transform_to_media_type(GstCaps *caps)
                     struct aac_user_data *user_data = NULL;
 
                     IMFMediaType_SetGUID(media_type, &MF_MT_SUBTYPE, &MFAudioFormat_AAC);
+                    IMFMediaType_SetUINT32(media_type, &MF_MT_AUDIO_BITS_PER_SAMPLE, 16);
 
                     codec_data = gst_structure_get_value(info, "codec_data");
                     if (codec_data)
-- 
2.25.3

From a917437ed5abb5bcdc61862fa232af491a692c6b Mon Sep 17 00:00:00 2001
From: Derek Lesho <dlesho@codeweavers.com>
Date: Fri, 1 May 2020 11:46:26 -0500
Subject: [PATCH 38/46] mfplat: Add I420 format information.

Signed-off-by: Derek Lesho <dlesho@codeweavers.com>
---
 dlls/mfplat/mediatype.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/dlls/mfplat/mediatype.c b/dlls/mfplat/mediatype.c
index 620e144d34..df1481b16e 100644
--- a/dlls/mfplat/mediatype.c
+++ b/dlls/mfplat/mediatype.c
@@ -1790,6 +1790,7 @@ static const struct uncompressed_video_format video_formats[] =
     { &MFVideoFormat_A2R10G10B10,   4, 3, 1, 0 },
     { &MFVideoFormat_RGB8,          1, 3, 1, 0 },
     { &MFVideoFormat_L8,            1, 3, 1, 0 },
+    { &MFVideoFormat_I420,          1, 0, 0, 1 },
     { &MFVideoFormat_AYUV,          4, 3, 0, 1 },
     { &MFVideoFormat_IMC1,          2, 3, 0, 1 },
     { &MFVideoFormat_IMC2,          1, 0, 0, 1 },
-- 
2.25.3

From cded62be23f01b8526f67d6cc8aee86e602a5fd6 Mon Sep 17 00:00:00 2001
From: Derek Lesho <dlesho@codeweavers.com>
Date: Fri, 1 May 2020 13:20:49 -0500
Subject: [PATCH 39/46] winegstreamer: Implement Color Converter MFT.

Signed-off-by: Derek Lesho <dlesho@codeweavers.com>
---
 dlls/winegstreamer/Makefile.in               |   1 +
 dlls/winegstreamer/colorconvert.c            | 704 +++++++++++++++++++
 dlls/winegstreamer/gst_private.h             |   2 +
 dlls/winegstreamer/mfplat.c                  |  35 +
 dlls/winegstreamer/winegstreamer_classes.idl |   8 +-
 5 files changed, 749 insertions(+), 1 deletion(-)
 create mode 100644 dlls/winegstreamer/colorconvert.c

diff --git a/dlls/winegstreamer/Makefile.in b/dlls/winegstreamer/Makefile.in
index e2af408582..a266b0a95e 100644
--- a/dlls/winegstreamer/Makefile.in
+++ b/dlls/winegstreamer/Makefile.in
@@ -6,6 +6,7 @@ EXTRALIBS = $(GSTREAMER_LIBS) $(PTHREAD_LIBS)
 PARENTSRC = ../strmbase ../mf
 
 C_SRCS = \
+	colorconvert.c \
 	filter.c \
 	gst_cbs.c \
 	gstdemux.c \
diff --git a/dlls/winegstreamer/colorconvert.c b/dlls/winegstreamer/colorconvert.c
new file mode 100644
index 0000000000..f149ab553f
--- /dev/null
+++ b/dlls/winegstreamer/colorconvert.c
@@ -0,0 +1,704 @@
+#include "config.h"
+#include <gst/gst.h>
+
+#include "gst_private.h"
+#include "gst_cbs.h"
+
+#include "mfapi.h"
+#include "mferror.h"
+#include "mfidl.h"
+
+#include "wine/debug.h"
+#include "wine/heap.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(mfplat);
+
+const GUID *raw_types[] = {
+    &MFVideoFormat_RGB24,
+    &MFVideoFormat_RGB32,
+    &MFVideoFormat_RGB555,
+    &MFVideoFormat_RGB8,
+    &MFVideoFormat_AYUV,
+    &MFVideoFormat_I420,
+    &MFVideoFormat_IYUV,
+    &MFVideoFormat_NV11,
+    &MFVideoFormat_NV12,
+    &MFVideoFormat_UYVY,
+    &MFVideoFormat_v216,
+    &MFVideoFormat_v410,
+    &MFVideoFormat_YUY2,
+    &MFVideoFormat_YVYU,
+    &MFVideoFormat_YVYU,
+};
+
+struct color_converter
+{
+    IMFTransform IMFTransform_iface;
+    LONG refcount;
+    IMFAttributes *attributes;
+    IMFAttributes *output_attributes;
+    IMFMediaType *input_type;
+    IMFMediaType *output_type;
+    BOOL valid_state, inflight;
+    GstElement *container, *appsrc, *videoconvert, *appsink;
+    GstBus *bus;
+    CRITICAL_SECTION cs;
+};
+
+static struct color_converter *impl_color_converter_from_IMFTransform(IMFTransform *iface)
+{
+    return CONTAINING_RECORD(iface, struct color_converter, IMFTransform_iface);
+}
+
+static HRESULT WINAPI color_converter_QueryInterface(IMFTransform *iface, REFIID riid, void **obj)
+{
+    TRACE("%p, %s, %p.\n", iface, debugstr_guid(riid), obj);
+
+    if (IsEqualIID(riid, &IID_IMFTransform) ||
+            IsEqualIID(riid, &IID_IUnknown))
+    {
+        *obj = iface;
+        IMFTransform_AddRef(iface);
+        return S_OK;
+    }
+
+    WARN("Unsupported %s.\n", debugstr_guid(riid));
+    *obj = NULL;
+    return E_NOINTERFACE;
+}
+
+static ULONG WINAPI color_converter_AddRef(IMFTransform *iface)
+{
+    struct color_converter *transform = impl_color_converter_from_IMFTransform(iface);
+    ULONG refcount = InterlockedIncrement(&transform->refcount);
+
+    TRACE("%p, refcount %u.\n", iface, refcount);
+
+    return refcount;
+}
+
+static ULONG WINAPI color_converter_Release(IMFTransform *iface)
+{
+    struct color_converter *transform = impl_color_converter_from_IMFTransform(iface);
+    ULONG refcount = InterlockedDecrement(&transform->refcount);
+
+    TRACE("%p, refcount %u.\n", iface, refcount);
+
+    if (!refcount)
+    {
+        if (transform->attributes)
+            IMFAttributes_Release(transform->attributes);
+        if (transform->output_attributes)
+            IMFAttributes_Release(transform->output_attributes);
+        heap_free(transform);
+    }
+
+    return refcount;
+}
+
+static HRESULT WINAPI color_converter_GetStreamLimits(IMFTransform *iface, DWORD *input_minimum, DWORD *input_maximum,
+        DWORD *output_minimum, DWORD *output_maximum)
+{
+    TRACE("%p, %p, %p, %p, %p.\n", iface, input_minimum, input_maximum, output_minimum, output_maximum);
+
+    *input_minimum = *input_maximum = *output_minimum = *output_maximum = 1;
+
+    return S_OK;
+}
+
+static HRESULT WINAPI color_converter_GetStreamCount(IMFTransform *iface, DWORD *inputs, DWORD *outputs)
+{
+    TRACE("%p, %p, %p.\n", iface, inputs, outputs);
+
+    *inputs = *outputs = 1;
+
+    return S_OK;
+}
+
+static HRESULT WINAPI color_converter_GetStreamIDs(IMFTransform *iface, DWORD input_size, DWORD *inputs,
+        DWORD output_size, DWORD *outputs)
+{
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI color_converter_GetInputStreamInfo(IMFTransform *iface, DWORD id, MFT_INPUT_STREAM_INFO *info)
+{
+    struct color_converter *converter = impl_color_converter_from_IMFTransform(iface);
+
+    TRACE("%p %u %p\n", converter, id, info);
+
+    if (id != 0)
+        return MF_E_INVALIDSTREAMNUMBER;
+
+    /* If we create a wrapped GstBuffer, remove MFT_INPUT_STREAM_DOES_NOT_ADDREF */
+    info->dwFlags = MFT_INPUT_STREAM_WHOLE_SAMPLES | MFT_INPUT_STREAM_DOES_NOT_ADDREF;
+    info->cbMaxLookahead = 0;
+    info->cbAlignment = 0;
+    /* this is incorrect */
+    info->hnsMaxLatency = 0;
+    return S_OK;
+}
+
+static HRESULT WINAPI color_converter_GetOutputStreamInfo(IMFTransform *iface, DWORD id, MFT_OUTPUT_STREAM_INFO *info)
+{
+    struct color_converter *converter = impl_color_converter_from_IMFTransform(iface);
+
+    MFT_OUTPUT_STREAM_INFO stream_info = {};
+
+    TRACE("%p %u %p\n", converter, id, info);
+
+    if (id != 0)
+        return MF_E_INVALIDSTREAMNUMBER;
+
+    stream_info.dwFlags = MFT_OUTPUT_STREAM_PROVIDES_SAMPLES;
+    stream_info.cbSize = 0;
+    stream_info.cbAlignment = 0;
+
+    *info = stream_info;
+
+    return S_OK;
+}
+
+static HRESULT WINAPI color_converter_GetAttributes(IMFTransform *iface, IMFAttributes **attributes)
+{
+    struct color_converter *transform = impl_color_converter_from_IMFTransform(iface);
+
+    TRACE("%p, %p.\n", iface, attributes);
+
+    *attributes = transform->attributes;
+    IMFAttributes_AddRef(*attributes);
+
+    return S_OK;
+}
+
+static HRESULT WINAPI color_converter_GetInputStreamAttributes(IMFTransform *iface, DWORD id,
+        IMFAttributes **attributes)
+{
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI color_converter_GetOutputStreamAttributes(IMFTransform *iface, DWORD id,
+        IMFAttributes **attributes)
+{
+    struct color_converter *transform = impl_color_converter_from_IMFTransform(iface);
+
+    TRACE("%p, %u, %p.\n", iface, id, attributes);
+
+    *attributes = transform->output_attributes;
+    IMFAttributes_AddRef(*attributes);
+
+    return S_OK;
+}
+
+static HRESULT WINAPI color_converter_DeleteInputStream(IMFTransform *iface, DWORD id)
+{
+    TRACE("%p, %u.\n", iface, id);
+
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI color_converter_AddInputStreams(IMFTransform *iface, DWORD streams, DWORD *ids)
+{
+    TRACE("%p, %u, %p.\n", iface, streams, ids);
+
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI color_converter_GetInputAvailableType(IMFTransform *iface, DWORD id, DWORD index,
+        IMFMediaType **type)
+{
+    IMFMediaType *ret;
+    HRESULT hr;
+
+    TRACE("%p, %u, %u, %p.\n", iface, id, index, type);
+
+    if (id != 0)
+        return MF_E_INVALIDSTREAMNUMBER;
+
+    if (index >= ARRAY_SIZE(raw_types))
+        return MF_E_NO_MORE_TYPES;
+
+    if (FAILED(hr = MFCreateMediaType(&ret)))
+        return hr;
+
+    if (FAILED(hr = IMFMediaType_SetGUID(ret, &MF_MT_MAJOR_TYPE, &MFMediaType_Video)))
+    {
+        IMFMediaType_Release(ret);
+        return hr;
+    }
+
+    if (FAILED(hr = IMFMediaType_SetGUID(ret, &MF_MT_SUBTYPE, raw_types[index])))
+    {
+        IMFMediaType_Release(ret);
+        return hr;
+    }
+
+    *type = ret;
+
+    return S_OK;
+}
+
+static void copy_attr(IMFMediaType *target, IMFMediaType *source, const GUID *key)
+{
+    PROPVARIANT val;
+
+    if (SUCCEEDED(IMFAttributes_GetItem((IMFAttributes *)source, key, &val)))
+    {
+        IMFAttributes_SetItem((IMFAttributes* )target, key, &val);
+    }
+}
+
+static HRESULT WINAPI color_converter_GetOutputAvailableType(IMFTransform *iface, DWORD id, DWORD index,
+        IMFMediaType **type)
+{
+    struct color_converter *converter = impl_color_converter_from_IMFTransform(iface);
+    IMFMediaType *output_type;
+    HRESULT hr;
+
+    TRACE("%p, %u, %u, %p.\n", iface, id, index, type);
+
+    if (id != 0)
+        return MF_E_INVALIDSTREAMNUMBER;
+
+    if (!(converter->input_type))
+        return MF_E_TRANSFORM_TYPE_NOT_SET;
+
+    if (index >= ARRAY_SIZE(raw_types))
+        return MF_E_NO_MORE_TYPES;
+
+    if (FAILED(hr = MFCreateMediaType(&output_type)))
+        return hr;
+
+    copy_attr(output_type, converter->input_type, &MF_MT_MAJOR_TYPE);
+    copy_attr(output_type, converter->input_type, &MF_MT_FRAME_SIZE);
+    copy_attr(output_type, converter->input_type, &MF_MT_FRAME_RATE);
+
+    if (FAILED(hr = IMFMediaType_SetGUID(output_type, &MF_MT_SUBTYPE, raw_types[index])))
+    {
+        IMFMediaType_Release(output_type);
+        return hr;
+    }
+
+    *type = output_type;
+
+    return S_OK;
+}
+
+static void color_converter_update_pipeline_state(struct color_converter *converter)
+{
+    converter->valid_state = converter->input_type && converter->output_type;
+
+    if (!converter->valid_state)
+    {
+        gst_element_set_state(converter->container, GST_STATE_READY);
+        return;
+    }
+
+    g_object_set(converter->appsrc, "caps", caps_from_mf_media_type(converter->input_type), NULL);
+    g_object_set(converter->appsink, "caps", caps_from_mf_media_type(converter->output_type), NULL);
+
+    gst_element_set_state(converter->container, GST_STATE_PLAYING);
+    return;
+}
+
+static HRESULT WINAPI color_converter_SetInputType(IMFTransform *iface, DWORD id, IMFMediaType *type, DWORD flags)
+{
+    HRESULT hr;
+
+    struct color_converter *converter = impl_color_converter_from_IMFTransform(iface);
+
+    TRACE("%p, %u, %p, %#x.\n", iface, id, type, flags);
+
+    if (id != 0)
+        return MF_E_INVALIDSTREAMNUMBER;
+
+    if (type)
+    {
+        GUID major_type, subtype;
+        BOOL found = FALSE;
+
+        if (FAILED(IMFMediaType_GetGUID(type, &MF_MT_MAJOR_TYPE, &major_type)))
+            return MF_E_INVALIDTYPE;
+        if (FAILED(IMFMediaType_GetGUID(type, &MF_MT_SUBTYPE, &subtype)))
+            return MF_E_INVALIDTYPE;
+
+        if (!(IsEqualGUID(&major_type, &MFMediaType_Video)))
+            return MF_E_INVALIDTYPE;
+
+        for (unsigned int i = 0; i < ARRAY_SIZE(raw_types); i++)
+        {
+            UINT64 unused;
+
+            if (IsEqualGUID(&subtype, raw_types[i]))
+            {
+                if (FAILED(IMFMediaType_GetUINT64(type, &MF_MT_FRAME_SIZE, &unused)))
+                    return MF_E_INVALIDTYPE;
+                found = TRUE;
+                break;
+            }
+        }
+
+        if (!found)
+            return MF_E_INVALIDTYPE;
+    }
+
+    if (flags & MFT_SET_TYPE_TEST_ONLY)
+    {
+        return S_OK;
+    }
+
+    hr = S_OK;
+
+    EnterCriticalSection(&converter->cs);
+
+    if (type)
+    {
+        if (!converter->input_type)
+            if (FAILED(hr = MFCreateMediaType(&converter->input_type)))
+                goto done;
+
+        if (FAILED(hr = IMFMediaType_CopyAllItems(type, (IMFAttributes *) converter->input_type)))
+            goto done;
+    }
+    else if (converter->input_type)
+    {
+        IMFMediaType_Release(converter->input_type);
+        converter->input_type = NULL;
+    }
+
+    done:
+    if (hr == S_OK)
+        color_converter_update_pipeline_state(converter);
+    LeaveCriticalSection(&converter->cs);
+
+    return S_OK;
+}
+
+static HRESULT WINAPI color_converter_SetOutputType(IMFTransform *iface, DWORD id, IMFMediaType *type, DWORD flags)
+{
+    struct color_converter *converter = impl_color_converter_from_IMFTransform(iface);
+    HRESULT hr;
+    GUID major_type, subtype;
+    UINT64 unused;
+
+    TRACE("%p, %u, %p, %#x.\n", iface, id, type, flags);
+
+    if (id != 0)
+        return MF_E_INVALIDSTREAMNUMBER;
+
+    if (type)
+    {
+        /* validate the type */
+
+        if (FAILED(IMFMediaType_GetGUID(type, &MF_MT_MAJOR_TYPE, &major_type)))
+            return MF_E_INVALIDTYPE;
+        if (FAILED(IMFMediaType_GetGUID(type, &MF_MT_SUBTYPE, &subtype)))
+            return MF_E_INVALIDTYPE;
+        if (FAILED(IMFMediaType_GetUINT64(type, &MF_MT_FRAME_SIZE, &unused)))
+            return MF_E_INVALIDTYPE;
+
+        if (!(IsEqualGUID(&major_type, &MFMediaType_Video)))
+            return MF_E_INVALIDTYPE;
+
+        for (unsigned int i = 0; i < ARRAY_SIZE(raw_types); i++)
+        {
+            if (IsEqualGUID(&subtype, raw_types[i]))
+                break;
+            if (i == ARRAY_SIZE(raw_types))
+                return MF_E_INVALIDTYPE;
+        }
+    }
+
+    if (flags & MFT_SET_TYPE_TEST_ONLY)
+    {
+        return S_OK;
+    }
+
+    EnterCriticalSection(&converter->cs);
+
+    hr = S_OK;
+
+    if (type)
+    {
+        if (!converter->output_type)
+            if (FAILED(hr = MFCreateMediaType(&converter->output_type)))
+                goto done;
+
+        if (FAILED(hr = IMFMediaType_CopyAllItems(type, (IMFAttributes *) converter->output_type)))
+            goto done;
+    }
+    else if (converter->output_type)
+    {
+        IMFMediaType_Release(converter->output_type);
+        converter->output_type = NULL;
+    }
+
+    done:
+    if (hr == S_OK)
+        color_converter_update_pipeline_state(converter);
+    LeaveCriticalSection(&converter->cs);
+
+    return hr;
+}
+
+static HRESULT WINAPI color_converter_GetInputCurrentType(IMFTransform *iface, DWORD id, IMFMediaType **type)
+{
+    FIXME("%p, %u, %p.\n", iface, id, type);
+
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI color_converter_GetOutputCurrentType(IMFTransform *iface, DWORD id, IMFMediaType **type)
+{
+    FIXME("%p, %u, %p.\n", iface, id, type);
+
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI color_converter_GetInputStatus(IMFTransform *iface, DWORD id, DWORD *flags)
+{
+    FIXME("%p, %u, %p.\n", iface, id, flags);
+
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI color_converter_GetOutputStatus(IMFTransform *iface, DWORD *flags)
+{
+    FIXME("%p, %p.\n", iface, flags);
+
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI color_converter_SetOutputBounds(IMFTransform *iface, LONGLONG lower, LONGLONG upper)
+{
+    FIXME("%p, %s, %s.\n", iface, wine_dbgstr_longlong(lower), wine_dbgstr_longlong(upper));
+
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI color_converter_ProcessEvent(IMFTransform *iface, DWORD id, IMFMediaEvent *event)
+{
+    TRACE("%p, %u, %p.\n", iface, id, event);
+
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI color_converter_ProcessMessage(IMFTransform *iface, MFT_MESSAGE_TYPE message, ULONG_PTR param)
+{
+    FIXME("%p, %u.\n", iface, message);
+
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI color_converter_ProcessInput(IMFTransform *iface, DWORD id, IMFSample *sample, DWORD flags)
+{
+    struct color_converter *converter = impl_color_converter_from_IMFTransform(iface);
+    GstBuffer *gst_buffer;
+    HRESULT hr = S_OK;
+    int ret;
+
+    TRACE("%p, %u, %p, %#x.\n", iface, id, sample, flags);
+
+    if (flags)
+        WARN("Unsupported flags %#x\n", flags);
+
+    if (id != 0)
+        return MF_E_INVALIDSTREAMNUMBER;
+
+    EnterCriticalSection(&converter->cs);
+
+    if (!converter->valid_state)
+    {
+        hr = MF_E_TRANSFORM_TYPE_NOT_SET;
+        goto done;
+    }
+
+    if (converter->inflight)
+    {
+        hr = MF_E_NOTACCEPTING;
+        goto done;
+    }
+
+    if (!(gst_buffer = gst_buffer_from_mf_sample(sample)))
+    {
+        hr = E_FAIL;
+        goto done;
+    }
+
+    g_signal_emit_by_name(converter->appsrc, "push-buffer", gst_buffer, &ret);
+    gst_buffer_unref(gst_buffer);
+    if (ret != GST_FLOW_OK)
+    {
+        ERR("Couldn't push buffer ret = %d\n", ret);
+        hr = E_FAIL;
+        goto done;
+    }
+
+    converter->inflight = TRUE;
+
+    done:
+    LeaveCriticalSection(&converter->cs);
+
+    return hr;
+}
+
+static HRESULT WINAPI color_converter_ProcessOutput(IMFTransform *iface, DWORD flags, DWORD count,
+        MFT_OUTPUT_DATA_BUFFER *samples, DWORD *status)
+{
+    struct color_converter *converter = impl_color_converter_from_IMFTransform(iface);
+    MFT_OUTPUT_DATA_BUFFER *relevant_buffer = NULL;
+    GstSample *sample;
+    HRESULT hr = S_OK;
+
+    TRACE("%p, %#x, %u, %p, %p.\n", iface, flags, count, samples, status);
+
+    if (flags)
+        WARN("Unsupported flags %#x\n", flags);
+
+    for (unsigned int i = 0; i < count; i++)
+    {
+        MFT_OUTPUT_DATA_BUFFER *out_buffer = &samples[i];
+
+        if (out_buffer->dwStreamID != 0)
+            return MF_E_INVALIDSTREAMNUMBER;
+
+        if (relevant_buffer)
+            return MF_E_INVALIDSTREAMNUMBER;
+
+        relevant_buffer = out_buffer;
+    }
+
+    if (!relevant_buffer)
+        return S_OK;
+
+    EnterCriticalSection(&converter->cs);
+
+    if (!converter->valid_state)
+    {
+        hr = MF_E_TRANSFORM_TYPE_NOT_SET;
+        goto done;
+    }
+
+    if (!converter->inflight)
+    {
+        hr = MF_E_TRANSFORM_NEED_MORE_INPUT;
+        goto done;
+    }
+
+    g_signal_emit_by_name(converter->appsink, "pull-sample", &sample);
+
+    converter->inflight =  FALSE;
+
+    relevant_buffer->pSample = mf_sample_from_gst_buffer(gst_sample_get_buffer(sample));
+    gst_sample_unref(sample);
+    relevant_buffer->dwStatus = S_OK;
+    relevant_buffer->pEvents = NULL;
+    *status = 0;
+
+    done:
+    LeaveCriticalSection(&converter->cs);
+
+    return hr;
+}
+
+static const IMFTransformVtbl color_converter_vtbl =
+{
+    color_converter_QueryInterface,
+    color_converter_AddRef,
+    color_converter_Release,
+    color_converter_GetStreamLimits,
+    color_converter_GetStreamCount,
+    color_converter_GetStreamIDs,
+    color_converter_GetInputStreamInfo,
+    color_converter_GetOutputStreamInfo,
+    color_converter_GetAttributes,
+    color_converter_GetInputStreamAttributes,
+    color_converter_GetOutputStreamAttributes,
+    color_converter_DeleteInputStream,
+    color_converter_AddInputStreams,
+    color_converter_GetInputAvailableType,
+    color_converter_GetOutputAvailableType,
+    color_converter_SetInputType,
+    color_converter_SetOutputType,
+    color_converter_GetInputCurrentType,
+    color_converter_GetOutputCurrentType,
+    color_converter_GetInputStatus,
+    color_converter_GetOutputStatus,
+    color_converter_SetOutputBounds,
+    color_converter_ProcessEvent,
+    color_converter_ProcessMessage,
+    color_converter_ProcessInput,
+    color_converter_ProcessOutput,
+};
+
+HRESULT color_converter_create(REFIID riid, void **ret)
+{
+    struct color_converter *object;
+    HRESULT hr;
+
+    TRACE("%s %p\n", debugstr_guid(riid), ret);
+
+    if (!(object = heap_alloc_zero(sizeof(*object))))
+        return E_OUTOFMEMORY;
+
+    object->IMFTransform_iface.lpVtbl = &color_converter_vtbl;
+    object->refcount = 1;
+
+    InitializeCriticalSection(&object->cs);
+
+    if (FAILED(hr = MFCreateAttributes(&object->attributes, 0)))
+        goto failed;
+
+    if (FAILED(hr = MFCreateAttributes(&object->output_attributes, 0)))
+        goto failed;
+
+    object->container = gst_bin_new(NULL);
+    object->bus = gst_bus_new();
+    gst_element_set_bus(object->container, object->bus);
+
+    if (!(object->appsrc = gst_element_factory_make("appsrc", NULL)))
+    {
+        ERR("Failed to create appsrc");
+        hr = E_FAIL;
+        goto failed;
+    }
+    gst_bin_add(GST_BIN(object->container), object->appsrc);
+
+    if (!(object->videoconvert = gst_element_factory_make("videoconvert", NULL)))
+    {
+        ERR("Failed to create converter\n");
+        hr = E_FAIL;
+        goto failed;
+    }
+    gst_bin_add(GST_BIN(object->container), object->videoconvert);
+
+    if (!(object->appsink = gst_element_factory_make("appsink", NULL)))
+    {
+        ERR("Failed to create appsink\n");
+        hr = E_FAIL;
+        goto failed;
+    }
+    gst_bin_add(GST_BIN(object->container), object->appsink);
+
+    if (!(gst_element_link(object->appsrc, object->videoconvert)))
+    {
+        ERR("Failed to link appsrc to videoconvert\n");
+        hr = E_FAIL;
+        goto failed;
+    }
+
+    if (!(gst_element_link(object->videoconvert, object->appsink)))
+    {
+        ERR("Failed to link videoconvert to appsink\n");
+        hr = E_FAIL;
+        goto failed;
+    }
+
+    *ret = &object->IMFTransform_iface;
+    return S_OK;
+
+failed:
+
+    IMFTransform_Release(&object->IMFTransform_iface);
+    return hr;
+}
\ No newline at end of file
diff --git a/dlls/winegstreamer/gst_private.h b/dlls/winegstreamer/gst_private.h
index 0b61bc9e3c..b855d650dc 100644
--- a/dlls/winegstreamer/gst_private.h
+++ b/dlls/winegstreamer/gst_private.h
@@ -76,4 +76,6 @@ enum source_type
 };
 HRESULT container_stream_handler_construct(REFIID riid, void **obj, enum source_type);
 
+HRESULT color_converter_create(REFIID riid, void **ret);
+
 #endif /* __GST_PRIVATE_INCLUDED__ */
diff --git a/dlls/winegstreamer/mfplat.c b/dlls/winegstreamer/mfplat.c
index b195bc0269..654e0351b2 100644
--- a/dlls/winegstreamer/mfplat.c
+++ b/dlls/winegstreamer/mfplat.c
@@ -429,6 +429,8 @@ static HRESULT asf_stream_handler_create(REFIID riid, void **ret)
     return container_stream_handler_construct(riid, ret, SOURCE_TYPE_ASF);
 }
 
+static GUID CLSID_CColorConvertDMO = {0x98230571,0x0087,0x4204,{0xb0,0x20,0x32,0x82,0x53,0x8e,0x57,0xd3}};
+
 static const struct class_object
 {
     const GUID *clsid;
@@ -442,6 +444,7 @@ class_objects[] =
     { &CLSID_CWMVDecMediaObject, &wmv_decoder_create },
     { &CLSID_MPEG4ByteStreamHandler, &mp4_stream_handler_create },
     { &CLSID_ASFByteStreamHandler, &asf_stream_handler_create },
+    { &CLSID_CColorConvertDMO, &color_converter_create },
 };
 
 HRESULT mfplat_get_class_object(REFCLSID rclsid, REFIID riid, void **obj)
@@ -473,6 +476,26 @@ HRESULT mfplat_get_class_object(REFCLSID rclsid, REFIID riid, void **obj)
     return CLASS_E_CLASSNOTAVAILABLE;
 }
 
+static WCHAR color_converterW[] = {'C','o','l','o','r',' ','C','o','n','v','e','r','t','e','r',0};
+
+const GUID *color_converter_supported_types[] =
+{
+    &MFVideoFormat_RGB24,
+    &MFVideoFormat_RGB32,
+    &MFVideoFormat_RGB555,
+    &MFVideoFormat_RGB8,
+    &MFVideoFormat_AYUV,
+    &MFVideoFormat_I420,
+    &MFVideoFormat_IYUV,
+    &MFVideoFormat_NV11,
+    &MFVideoFormat_NV12,
+    &MFVideoFormat_UYVY,
+    &MFVideoFormat_v216,
+    &MFVideoFormat_v410,
+    &MFVideoFormat_YUY2,
+    &MFVideoFormat_YVYU,
+    &MFVideoFormat_YVYU,
+};
 
 static WCHAR h264decoderW[] = {'H','.','2','6','4',' ','D','e','c','o','d','e','r',0};
 const GUID *h264_decoder_input_types[] =
@@ -538,6 +561,18 @@ static const struct mft
 }
 mfts[] =
 {
+    {
+        &CLSID_CColorConvertDMO,
+        &MFT_CATEGORY_VIDEO_EFFECT,
+        color_converterW,
+        MFT_ENUM_FLAG_SYNCMFT,
+        &MFMediaType_Video,
+        ARRAY_SIZE(color_converter_supported_types),
+        color_converter_supported_types,
+        ARRAY_SIZE(color_converter_supported_types),
+        color_converter_supported_types,
+        NULL
+    },
     {
         &CLSID_CMSH264DecoderMFT,
         &MFT_CATEGORY_VIDEO_DECODER,
diff --git a/dlls/winegstreamer/winegstreamer_classes.idl b/dlls/winegstreamer/winegstreamer_classes.idl
index 376e08c418..d933d40cfd 100644
--- a/dlls/winegstreamer/winegstreamer_classes.idl
+++ b/dlls/winegstreamer/winegstreamer_classes.idl
@@ -84,4 +84,10 @@ coclass ASFByteStreamHandler {}
     threading(both),
     uuid(82d353df-90bd-4382-8bc2-3f6192b76e34)
 ]
-coclass CLSID_CWMVDecMediaObject {}
\ No newline at end of file
+coclass CLSID_CWMVDecMediaObject {}
+
+[
+    threading(both),
+    uuid(98230571-0087-4204-b020-3282538e57d3)
+]
+coclass CColorConvertDMO { }
\ No newline at end of file
-- 
2.25.3

From 87ac938a34eb4a9e524ccc8077ed9971f66081cb Mon Sep 17 00:00:00 2001
From: Derek Lesho <dlesho@codeweavers.com>
Date: Fri, 1 May 2020 22:36:02 -0500
Subject: [PATCH 40/46] HACK: Set BPS to 16 for output template.

---
 dlls/winegstreamer/mf_decode.c | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/dlls/winegstreamer/mf_decode.c b/dlls/winegstreamer/mf_decode.c
index d44bdb5f4b..5de1b60d2a 100644
--- a/dlls/winegstreamer/mf_decode.c
+++ b/dlls/winegstreamer/mf_decode.c
@@ -321,6 +321,11 @@ static HRESULT WINAPI mf_decoder_GetOutputAvailableType(IMFTransform *iface, DWO
         return hr;
     }
 
+    if (IsEqualGUID(decoder_descs[decoder->type].output_types[index], &MFAudioFormat_PCM))
+    {
+        IMFMediaType_SetUINT32(output_type, &MF_MT_AUDIO_BITS_PER_SAMPLE, 16);
+    }
+
     *type = output_type;
 
     return S_OK;
-- 
2.25.3

From a1dda3212c2601f2e74805f2f82e450361b2b329 Mon Sep 17 00:00:00 2001
From: Derek Lesho <dlesho@codeweavers.com>
Date: Mon, 4 May 2020 12:40:56 -0500
Subject: [PATCH 41/46] Set "wait-on-eos" to allow other streams to continue
 while appsink buffers.

---
 dlls/winegstreamer/media_source.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/dlls/winegstreamer/media_source.c b/dlls/winegstreamer/media_source.c
index 5c01346987..5a8537970a 100644
--- a/dlls/winegstreamer/media_source.c
+++ b/dlls/winegstreamer/media_source.c
@@ -1037,6 +1037,7 @@ static HRESULT media_stream_constructor(struct media_source *source, GstPad *pad
     g_object_set(object->appsink, "emit-signals", TRUE, NULL);
     g_object_set(object->appsink, "sync", FALSE, NULL);
     g_object_set(object->appsink, "async", FALSE, NULL); /* <- This allows us interact with the bin w/o prerolling */
+    g_object_set(object->appsink, "wait-on-eos", FALSE, NULL);
     g_signal_connect(object->appsink, "new-sample", G_CALLBACK(stream_new_sample_wrapper), object);
 
     if (FAILED(hr = media_stream_align_with_mf(object, &stream_type)))
-- 
2.25.3

From a3318b23c4c10aa2adf98a213b5c1af8b6728ff1 Mon Sep 17 00:00:00 2001
From: Derek Lesho <dlesho@codeweavers.com>
Date: Mon, 4 May 2020 12:42:09 -0500
Subject: [PATCH 42/46] Stub out enough of the SAR to prevent the session from
 stalling.

---
 dlls/mf/sar.c | 19 +++++++++++++++++++
 1 file changed, 19 insertions(+)

diff --git a/dlls/mf/sar.c b/dlls/mf/sar.c
index f883163bde..d5998cb1f3 100644
--- a/dlls/mf/sar.c
+++ b/dlls/mf/sar.c
@@ -553,6 +553,7 @@ static HRESULT WINAPI audio_renderer_clock_sink_OnClockStart(IMFClockStateSink *
             if (FAILED(hr = IAudioClient_Start(renderer->audio_client)))
                 WARN("Failed to start audio client, hr %#x.\n", hr);
             renderer->state = STREAM_STATE_RUNNING;
+            IMFStreamSink_QueueEvent(&renderer->IMFStreamSink_iface, MEStreamSinkRequestSample, &GUID_NULL, S_OK, NULL);
         }
     }
     else
@@ -1239,16 +1240,34 @@ static HRESULT WINAPI audio_renderer_stream_GetMediaTypeHandler(IMFStreamSink *i
 
 static HRESULT WINAPI audio_renderer_stream_ProcessSample(IMFStreamSink *iface, IMFSample *sample)
 {
+    struct audio_renderer *renderer = impl_from_IMFStreamSink(iface);
+
     FIXME("%p, %p.\n", iface, sample);
 
+    EnterCriticalSection(&renderer->cs);
+
+    if (renderer->state == STREAM_STATE_RUNNING && sample)
+        IMFStreamSink_QueueEvent(&renderer->IMFStreamSink_iface, MEStreamSinkRequestSample, &GUID_NULL, S_OK, NULL);
+
+    LeaveCriticalSection(&renderer->cs);
+
     return E_NOTIMPL;
 }
 
 static HRESULT WINAPI audio_renderer_stream_PlaceMarker(IMFStreamSink *iface, MFSTREAMSINK_MARKER_TYPE marker_type,
         const PROPVARIANT *marker_value, const PROPVARIANT *context_value)
 {
+    struct audio_renderer *renderer = impl_from_IMFStreamSink(iface);
+
     FIXME("%p, %d, %p, %p.\n", iface, marker_type, marker_value, context_value);
 
+    EnterCriticalSection(&renderer->cs);
+
+    if (renderer->state == STREAM_STATE_RUNNING)
+        IMFStreamSink_QueueEvent(&renderer->IMFStreamSink_iface, MEStreamSinkMarker, &GUID_NULL, S_OK, context_value);
+
+    LeaveCriticalSection(&renderer->cs);
+
     return E_NOTIMPL;
 }
 
-- 
2.25.3

From 2af061f28f61b6067c6b1ed2cc5c0fff86e3e21b Mon Sep 17 00:00:00 2001
From: Derek Lesho <dlesho@codeweavers.com>
Date: Mon, 4 May 2020 12:42:36 -0500
Subject: [PATCH 43/46] Finalize the sinks when transitioning from
 STARTED/PAUSED to CLOSED.

---
 dlls/mf/session.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/dlls/mf/session.c b/dlls/mf/session.c
index 68720247fc..db9f46e579 100644
--- a/dlls/mf/session.c
+++ b/dlls/mf/session.c
@@ -923,6 +923,7 @@ static void session_close(struct media_session *session)
             session->presentation.flags |= SESSION_FLAG_FINALIZE_SINKS;
             if (SUCCEEDED(hr = IMFPresentationClock_Stop(session->clock)))
                 session->state = SESSION_STATE_STOPPING_SINKS;
+            hr = session_finalize_sinks(session);
             break;
         case SESSION_STATE_CLOSED:
             hr = MF_E_INVALIDREQUEST;
-- 
2.25.3

From 1cb5f35751016f90663b39ab9715136eb81d7f90 Mon Sep 17 00:00:00 2001
From: Derek Lesho <dlesho@codeweavers.com>
Date: Tue, 5 May 2020 15:35:16 -0500
Subject: [PATCH 46/46] Report streams backwards and only select one of each
 stream type.

---
 dlls/winegstreamer/media_source.c | 24 ++++++++++++++++++++++--
 1 file changed, 22 insertions(+), 2 deletions(-)

diff --git a/dlls/winegstreamer/media_source.c b/dlls/winegstreamer/media_source.c
index 5a8537970a..f62fb6716b 100644
--- a/dlls/winegstreamer/media_source.c
+++ b/dlls/winegstreamer/media_source.c
@@ -1430,6 +1430,7 @@ static HRESULT media_source_constructor(IMFByteStream *bytestream, enum source_t
         source_descs[type].bytestream_caps);
 
     struct media_source *object = heap_alloc_zero(sizeof(*object));
+    BOOL video_selected = FALSE, audio_selected = FALSE;
     GList *demuxer_list_one, *demuxer_list_two;
     GstElementFactory *demuxer_factory = NULL;
     IMFStreamDescriptor **descriptors = NULL;
@@ -1527,15 +1528,34 @@ static HRESULT media_source_constructor(IMFByteStream *bytestream, enum source_t
     descriptors = heap_alloc(object->stream_count * sizeof(IMFStreamDescriptor*));
     for (unsigned int i = 0; i < object->stream_count; i++)
     {
-        IMFMediaStream_GetStreamDescriptor(&object->streams[i]->IMFMediaStream_iface, &descriptors[i]);
+        IMFMediaStream_GetStreamDescriptor(&object->streams[i]->IMFMediaStream_iface, &descriptors[object->stream_count - 1 - i]);
     }
 
     if (FAILED(MFCreatePresentationDescriptor(object->stream_count, descriptors, &object->pres_desc)))
         goto fail;
 
+    /* Select one of each major type. */
     for (unsigned int i = 0; i < object->stream_count; i++)
     {
-        IMFPresentationDescriptor_SelectStream(object->pres_desc, i);
+        IMFMediaTypeHandler *handler;
+        GUID major_type;
+        BOOL select_stream = FALSE;
+
+        IMFStreamDescriptor_GetMediaTypeHandler(descriptors[i], &handler);
+        IMFMediaTypeHandler_GetMajorType(handler, &major_type);
+        if (IsEqualGUID(&major_type, &MFMediaType_Video) && !video_selected)
+        {
+            select_stream = TRUE;
+            video_selected = TRUE;
+        }
+        if (IsEqualGUID(&major_type, &MFMediaType_Audio) && !audio_selected)
+        {
+            select_stream = TRUE;
+            audio_selected = TRUE;
+        }
+        if (select_stream)
+            IMFPresentationDescriptor_SelectStream(object->pres_desc, i);
+        IMFMediaTypeHandler_Release(handler);
         IMFStreamDescriptor_Release(descriptors[i]);
     }
     heap_free(descriptors);
-- 
2.25.3

